VerifyError: Uninitialized object exists on backwa

2020-06-06 08:08发布

问题:

The JVM Spec 4.10.2.4 version 7, last paragraph, says

A valid instruction sequence must not have an uninitialized object on the operand stack or in a local variable at the target of a backwards branch if the special type of the uninitialized object is merged with a special type other than itself

Here's an example rejected by the verifier - I suspect that it should be accepted:

public scala.Tuple2<scala.runtime.Null$, scala.runtime.Null$> apply(boolean);
  flags: ACC_PUBLIC
  Code:
    stack=4, locals=2, args_size=2
       0: new           #12                 // class scala/Tuple2
       3: dup           
       4: aconst_null   
       5: iload_1       
       6: ifne          5
       9: aconst_null   
      10: invokespecial #16                 // Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      13: areturn       
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
             0      14     0  this   LC;
             0      14     1     x   Z
    StackMapTable: number_of_entries = 1
         frame_type = 255 /* full_frame */
        offset_delta = 5
        locals = [ class C, int ]
        stack = [ uninitialized 0, uninitialized 0, null ]

The error message complains about the backwards jump ifne 5

java.lang.VerifyError: Uninitialized object exists on backward branch 5
Exception Details:
  Location:
    C.apply(Z)Lscala/Tuple2; @6: ifne

There is indeed an uninitialized object on the stack at the jump target; however, it looks to me that the "special type of the uninitialized object" is merged with itself, just as required by the spec.

I think there is only one stack map frame, so it cannot be merged with anything else.

Interestingly, the restriction on backwards branches was removed in the JVM Spec version 8.

However, the Verifier in Java 8 VM still rejects the example.

Did I misread the JVM spec, or should the example really fail verification? I tried versions 1.7.0_60-b19 and 1.8.0_05-b13.


General Research

The issue shows up in Scala (bugreport). To reproduce, take scala 2.11.1, make sure you're on a JVM >= 1.7 and run the following (be sure to pass -target:jvm-1.7 to scala):

localhost:sandbox luc$ scala -target:jvm-1.7
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class C {
     |   def apply(x: Boolean) = new Tuple2(null, {
     |     while (x) { }
     |     null
     |   })
     | }
defined class C

scala> new C
java.lang.VerifyError: Uninitialized object exists on backward branch 5
Exception Details:
  Location:
    C.apply(Z)Lscala/Tuple2; @6: ifne
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: bb00 0959 011b 9aff ff01 b700 0db0
  Stackmap Table:
    full_frame(@5,{Object[#2],Integer},{Uninitialized[#0],Uninitialized[#0],Null})

  ... 32 elided

As mentioned above - JDK bugreport here, I hope to get a response there.

The JDK bug was fixed in JDK 8u25

回答1:

The JDK bug was fixed in JDK 8u25