JDK-7017493 : ConcurrentLinkedDeque: Unexpected initialization order can lead to crash due to use of Unsafe
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2011-02-06
  • Updated: 2012-02-02
  • Resolved: 2011-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b134Fixed
Related Reports
Relates :  
Description
Like many java.util.concurrent classes the ConcurrentLinkedDeque (and its nested Node class) makes use of sun.misc.Unsafe to implement lock-free algorithms. Under normal usage ConcurrentLinkedDeque is always initialized before its static Node class. However some tools (or direct use of Class.forName) can trigger initialization of the Node class before the outer ConcurrentLinkedDeque class, and when this occurs we can trigger a VM crash. The problem is the following sequence:

Node has 3 static "offset" fields initialized similarly, e.g.: 
 
        private static final long itemOffset = 
            objectFieldOffset(UNSAFE, "item", Node.class); 
 
objectFieldOffset is a static method in CLDeque, so the Node clinit 
causes initialization of CLDeque.  CLDeque has this static block: 
 
    static { 
        PREV_TERMINATOR = new Node<Object>(null); 
        PREV_TERMINATOR.next = PREV_TERMINATOR; 
        NEXT_TERMINATOR = new Node<Object>(null); 
        NEXT_TERMINATOR.prev = NEXT_TERMINATOR; 
    } 

So PREV_TERMINATOR and NEXT_TERMINATOR are Nodes that are created before 
the Node clinit completes, and the Node ctor uses itemOffset: 
 
        Node(E item) { 
            UNSAFE.putObject(this, itemOffset, item); 
        } 

but itemOffset is unitialized and thus 0, so the mark word is overwritten.

When the GC enounters the Node instances with a zero markword it can crash the VM.

Comments
EVALUATION Changeset: 892c3fc7249e Author: dl Date: 2011-02-23 14:56 +0000 URL: http://hg.openjdk.java.net/jdk7/tl/jdk/rev/892c3fc7249e 7017493: ConcurrentLinkedDeque: Unexpected initialization order can lead to crash due to use of Unsafe Reviewed-by: chegar ! src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java ! src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java ! src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java ! src/share/classes/java/util/concurrent/ConcurrentSkipListSet.java ! src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java ! src/share/classes/java/util/concurrent/LinkedTransferQueue.java ! src/share/classes/java/util/concurrent/Phaser.java ! src/share/classes/java/util/concurrent/PriorityBlockingQueue.java ! src/share/classes/java/util/concurrent/SynchronousQueue.java
23-02-2011

EVALUATION Doug Lea writes: I went through and replaced nearly all non-manifest-constant inline final static inititializers with static blocks ordered to reduce dependencies. All but a couple were Unsafe field offset inits. Dealing with the initial problematic case ConcurrentLinkedDeque was best done by also adding a default node constructor (as previously suggested) to ensure independence even if static init order is somehow changed someday. The boring changes are checked into our CVS for classes: ConcurrentLinkedDeque.java ConcurrentLinkedQueue.java ConcurrentSkipListMap.java ConcurrentSkipListSet.java CopyOnWriteArrayList.java LinkedTransferQueue.java Phaser.java PriorityBlockingQueue.java SynchronousQueue.java
23-02-2011

EVALUATION There are a number of ways to fix this. One initial comment is to note that the reason the Node constructor has the form: Node(E item) { UNSAFE.putObject(this, itemOffset, item); } rather than simply: Node(E item) { this.item = item; } is to optimize the (compiled version of the) constructor so that an unnecessary volatile store is not used. If not for the use of Unsafe in the constructor there would not be a problem. So one solution is to add a special constructor for the case of a null item (as used by the CLDQ terminator nodes): Node() {} Alternatively we could replicate the CLDQ objectFieldOffset method into Node so that Node does not trigger initialization of CLDQ. Or we can simply rework the Unsafe usage across this and a number of other classes to ensure we don't encounter a similar problem elsewhere.
06-02-2011