JDK-8180615 : monitorenter on null object produces unexpected IllegalMonitorStateException
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 7,8,9,10
  • Priority: P3
  • Status: Resolved
  • Resolution: Not an Issue
  • Submitted: 2017-05-18
  • Updated: 2017-05-22
  • Resolved: 2017-05-22
Related Reports
Relates :  
Description
http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2017-May/023453.html

super class Foo
	version 52:0
{

...
static Method a:"(Ljava/lang/Object;)V"
	stack 2 locals 3
{
		aload_0;
		monitorenter;
                aconst_null;
        	monitorenter;
                aconst_null;
        	monitorexit;
		aload_0;
		monitorexit;
		return;
}

When calling Foo.a, https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.11.10 says that NullPointerException should be thrown. However, the JDK throws IllegalMonitorStateException:

===============
OUTPUT from JDK8u92:

java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

Calling Foo.a()
java.lang.IllegalMonitorStateException
	at Foo.a(Foo.s)
	at A.main(A.java:16)
Calling Foo.b()
java.lang.NullPointerException
	at Foo.b(Foo.s)
	at A.main(A.java:22)

Comments
For the cases where we see IMSE being thrown there is in fact an initial NPE eg from exception logging: Exception <a 'java/lang/NullPointerException'> (0xd156c6b8) thrown in interpreter method <{method} {0xa1eda4e8} 'main' '([Ljava/lang/String;)V' in 'Search'> at bci 3 for thread 0xf5f09400 Exception <a 'java/lang/IllegalMonitorStateException'> (0xd156c868) thrown in interpreter method <{method} {0xa1eda190} 'main' '([Ljava/lang/String;)V' in 'T'> at bci 10 for thread 0xf5f09400 What is happening in the case of Foo.a(args) is that we have a successful monitorenter followed by an invalid monitorenter. The invalid monitorenter throws NPE as expected and the call stack unwinds. As we leave the method frame the system can see that we still have a locked monitor, which means we have skipped an unlock and so have violated the "structured locking" requirements that a VM can choose to enforce. Because of that we throw the IllegalMonitorStateException, which replaces the NullPointerException that was in the process of being thrown. So this is all working exactly as intended/designed. If we look at Yutings examples we see similar problems: Case 1: 0: aload_0 1: monitorenter 2: aconst_null 3: monitorexit 4: return We have a successful monitorenter then an invalid monitorexit that throws NPE, but as we leave the frame we violate structured locking and so throw IllegalMonitorStateException. Case 2: 0: aload_0 1: monitorenter 2: aconst_null 3: monitorenter 4: aconst_null 5: monitorexit 6: aload_0 7: monitorexit 8: return This is similar to Ioi's example: successful monitorenter followed by invalid monitorenter that throws NPE, but as we leave the frame we violate structured locking and so throw IllegalMonitorStateException. Case 3: 0: new #2 // class Search 3: dup 4: aload_0 5: monitorenter 6: monitorenter 7: aload_0 8: monitorexit 9: monitorexit 10: return This is the verifier test. But there is no verification failure and so we have legal and paired monitorenter and monitorexits, so no exceptions thrown. Case 4: 0: new #2 // class Search 3: invokespecial #14 // Method "<init>":()V 6: aload_0 7: monitorenter 8: aconst_null 9: monitorenter 10: aload_0 11: monitorexit 12: aconst_null 13: monitorexit 14: return Again a valid monitorenter followed by an invalid one that throws NPE, but as we leave the frame we violate structured locking and so throw IllegalMonitorStateException. case 5: 0: new #2 // class Search 3: astore_1 4: aload_1 5: invokespecial #14 // Method "<init>":()V 8: aload_0 9: monitorenter 10: aload_1 11: monitorenter 12: aload_1 13: monitorexit 14: aconst_null 15: monitorenter 16: aload_0 17: monitorexit 18: aconst_null 19: monitorexit 20: return Same pattern. We have a validly locked monitor at the time we hit the first invalid monitorenter at #15, so the NPE from #15 gets replaced by the IllegalMonitorStateException as we leave the frame.
22-05-2017

Yes two issues - though runtime behaviour of something that should be caught by verifier is not something that needs fixing. I was waiting to test these different scenarios myself before filing a bug - but now its here I've taken this.
18-05-2017

[~dcubed] - it's a different issue. JDK-8180581 is "Verifier fails to detect "uninitialized" target of monitorenter/exit bytecode" In my this bug, all objects passed to monitorenter are either NULL or initialized.
18-05-2017

Test case: A.java, A.class, Foo.s, Foo.class See comments in A.java for build/test instruction. If you don't have ASMTOOLS, you can just use the attached class files (works for JDK 7 and higher): $ java -cp . A Calling Foo.a() java.lang.IllegalMonitorStateException at Foo.a(Foo.s) at A.main(A.java:33) Calling Foo.b() java.lang.NullPointerException at Foo.b(Foo.s) at A.main(A.java:39)
18-05-2017

David already filed JDK-8180581 for this issue.
18-05-2017