JDK-8180581 : Verifier fails to detect "uninitialized" target of monitorenter/exit bytecode
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 9,10
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • Submitted: 2017-05-18
  • Updated: 2025-08-28
  • Resolved: 2017-05-22
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
See email discussion: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2017-May/023453.html

Summary of problem:

If we create bytecode that does a "new" but which then fails to call invokespecial with a constructor, and uses that "reference" for a monitorenter or monitorexit, the hotspot verifier does not complain. In constrast IBM's J9 fails verification with:

Exception in thread "main" java.lang.VerifyError: JVMVRFY012 stack shape
inconsistent; class=Search, method=main([Ljava/lang/String;)V, pc=6
Exception Details:
  Location:
    Search.main([Ljava/lang/String;)V @6: JBmonitorenter
  Reason:
    Type 'uninitialized' (current frame, stack[1]) is not assignable to
'java/lang/Object'
  Current Frame:
    bci: @6
    flags: { }
    locals: { 'Search', '[Ljava/lang/String;' }
    stack: { 'uninitialized', 'uninitialized' }
	at T.main(T.java:4)




Comments
After some robust discussions, examination of historical documents and after further contemplation of the implications I am closing this as not an issue. From my perspective, there are two points to address: 1. Did the JVMS really intend to use "reference" in the verification rules and thus permit use of "uninitialized"? The formal verifier is defined in JVMS 4.10.1 as introduced by JSR-202. There is nothing to suggest any non-intent to use "reference" where it is used within that formal specification. From a formalism perspective there is nothing invalid about allowing uninitailized in such contexts. Indeed it is required if one considers that in a constructor, where the type of 'this' is 'uninitialized_this', we can do a monitorenter on 'this'. 2. Is there any potential for harm in doing a monitorenter/exit on an "uninitialized" instance? The simple answer is No. After a "new" we have allocated a class instance for which all fields have their default initialized values. The execution of a constructor to establish initial state and invariants has no bearing on the ability to perform a monitorenter/exit on that instance. Indeed for a class with an empty constructor there is no difference between the actual instance before and after that constructor call. The semantics of monitors are not in any way tied to the need to execute any constructor and indeed they can not be given that such constructors may perform monitor actions at any time. It follows that the instance must be supportive of monitor actions upon successful completion of the "new". So this is not an issue with respect to hotspot as we follow the specification as written and allow the action to pass the verifier. If there are questions about the verification rules, or an argument that the rules are incorrect in some way then that should be addressed to jls-jvms-spec-comments@openjdk.java.net.
22-05-2017

It seems to me, as a lay person reading the spec, that 'reference' is sometimes used in contexts where 'uninitialized' is precluded. For example: "If the array types have the same dimensions, then the merged value is a _reference_ to an instance of an array type which is first common supertype of both array types." [4.10.2.2] So did it really mean to say "reference" as now defined in 4.10.1? In JVMS 2 the word "reference" is defined simply as a Java reference: class types, array types, interface types or null. And many of the places where JVMS 2 uses "reference" also say "reference" in the current JVMS. I can easily imagine that some uses of "reference" were carried over into 4.10.1 without realising the definition of 'reference' was no longer the same. Looking elsewhere for some hint as to intent/expectation we have statements as in 4.10.2.2: • If the instruction uses values from the operand stack, ensure that there are a sufficient number of values on the stack and that the top values on the stack are of an appropriate type. Otherwise, verification fails. • If the instruction uses a local variable, ensure that the specified local variable contains a value of the appropriate type. Otherwise, verification fails. but what is meant by "appropriate" here? Given this description: "For each local variable, it needs to know either the type of the contents of that local variable or that the local variable contains an unusable or unknown value (it might be uninitialized)." it would not be unreasonable to think that an 'uninitialized' is not "appropriate". Similarly given: "All other local variables contain an illegal value." might suggest that use of that local variable, being an "illegal value" would not be "appropriate". In 4.10.2.4 there is a general statement: "The verifier rejects code that uses the new object before it has been initialized ..." So one might then expect the monitorenter or monitorexit of such a new object to rejected.
20-05-2017

I agree that monitorenter's specification in 4.10.1.2 demands merely that a type matching 'reference' can be popped, not a Java reference type such as Object. Consequently, a type matching 'uninitialized' may be popped. The policy of demanding merely 'reference' is also found in aload, areturn, astore, if_acmp, and ifnonnull, which makes me suspect it was deliberate. If further evidence suggests that 'reference' is undesirable in these instructions, please file a specification/vm RFE.
19-05-2017

I agree this needs clarification. I think it should be explicit that a "type matching reference" actually means a Java reference - or that at least it can not be an "uniitialized". checkcast and instanceof are somewhat different because they refer to a CP entry that itself must refer to a class or array and so by definition can not be "uninitialized". Looking at other bytecodes that operate on an object (getfiled, putfield etc). It isn't clear to me that any of these disallow an "uninitialized" from appearing on the stack. But I admit I really do not know how to fully read and interpret the verification rules.
19-05-2017

This may require some Spec clarification. Based on this text from the JVMS the monitorenter instruction is valid: JVMSpec-8, section 4.10.1.9 says: A monitorenter instruction is type safe iff one can validly pop a type matching reference off the incoming operand stack. The verification type hierarchy diagram in section 4.10.1.2 of JVMSpec-8 shows that uninitialized is a reference. So, for this monitorenter instruction, the value on the stack is of an appropriate type, a reference. In contrast to checkcast and instanceof, there is nothing in the spec that says that what's on the stack for monitorenter must be assignable to java.lang.Object.
18-05-2017