JDK-8172282 : Inconsistent class verification of initializers
  • Type: Bug
  • Component: core-svc
  • Sub-Component: java.lang.instrument
  • Affected Version: 8,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2017-01-03
  • Updated: 2017-01-30
  • Resolved: 2017-01-30
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 9
9Resolved
Description
FULL PRODUCT VERSION :
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b16, mixed mode)

FULL OS VERSION :
Darwin <redacted> 16.1.0 Darwin Kernel Version 16.1.0: Thu Oct 13 21:26:57 PDT 2016; root:xnu-3789.21.3~60/RELEASE_X86_64 x86_64

EXTRA RELEVANT SYSTEM CONFIGURATION :
macOS Sierra 10.12.1

A DESCRIPTION OF THE PROBLEM :
The methods in the following code are instrumented to enclose the entire method body in a try-catch block.  With this instrumentation, X(String) constructor passes class verification at runtime while X(X) constructor fails class verification with VerifyError -- Current frame's flags are not assignable to stack map frame's.

Since the bodies of the constructor is identical, both constructors should produce identical results under identical instrumentation; however, this doesn't seem to be the case.

public class X extends Y {
    X(String s) { // does not fail
        super(new Y(3));
    }
    X(X s) {  // fails
        super(new Y(3));
    }

  /*
    public static void main(final String[] s) {
        new X("Hi");
    }
    */
}

class Y {
    Y(Object o) {}
    Y(int i) {}
}



THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Yes

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the following commands with ASM version 5.1 (http://forge.ow2.org/project/showfiles.php?group_id=23) and the provided source placed in XDump.java.

javac -cp <path/to/asm-all-5.1.jar> XDump.java
java -cp <path/to/asm-all-5.1.jar>:. XDump 
java -cp . X

EXPECTED VERSUS ACTUAL BEHAVIOR :
Instead of the class being successfully loaded, VerifyError is encountered while verifying X(X) method but not while verifying X(String)
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: Stack map does not match the one at exception handler 13
Exception Details:
  Location:
    X.<init>(LX;)V @13: athrow
  Reason:
    Current frame's flags are not assignable to stack map frame's.
  Current Frame:
    bci: @0
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'X' }
    stack: { 'java/lang/Throwable' }
  Stackmap Frame:
    bci: @13
    flags: { }
    locals: { top, 'X' }
    stack: { 'java/lang/Throwable' }
  Bytecode:
    0x0000000: 2abb 0004 5906 b700 09b7 000c b1bf
  Exception Handler Table:
    bci [0, 13] => handler: 13
  Stackmap Table:
    full_frame(@13,{Top,Object[#2]},{Object[#14]})

	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
public class XDump {

  public static void main(String[] s) throws Exception {
    byte[] b = dump();
    Files.write(new File("./X.class").toPath(), b);
  }

  public static byte[] dump () throws Exception {

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    FieldVisitor fv;
    MethodVisitor mv;
    AnnotationVisitor av0;

    cw.visit(52, ACC_PUBLIC + ACC_SUPER, "X", null, "Y", null);
    
    {
    mv = cw.visitMethod(0, "<init>", "(Ljava/lang/String;)V", null, null);
    mv.visitCode();
    Label begin = new Label();
    mv.visitLabel(begin);
    mv.visitVarInsn(ALOAD, 0);
    mv.visitTypeInsn(NEW, "Y");
    mv.visitInsn(DUP);
    mv.visitInsn(ICONST_3);
    mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(I)V", false);
    mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(Ljava/lang/Object;)V", false);
    mv.visitInsn(RETURN);
    Label end = new Label();
    mv.visitLabel(end);
    mv.visitTryCatchBlock(begin, end, end, "java/lang/Throwable");
    mv.visitInsn(Opcodes.ATHROW);
    mv.visitMaxs(4, 2);
    mv.visitEnd();
    }

    {
    mv = cw.visitMethod(0, "<init>", "(LX;)V", null, null);
    mv.visitCode();
    Label begin = new Label();
    mv.visitLabel(begin);
    mv.visitVarInsn(ALOAD, 0);
    mv.visitTypeInsn(NEW, "Y");
    mv.visitInsn(DUP);
    mv.visitInsn(ICONST_3);
    mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(I)V", false);
    mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(Ljava/lang/Object;)V", false);
    mv.visitInsn(RETURN);
    Label end = new Label();
    mv.visitLabel(end);
    mv.visitTryCatchBlock(begin, end, end, "java/lang/Throwable");
    mv.visitInsn(Opcodes.ATHROW);
    mv.visitMaxs(4, 2);
    mv.visitEnd();
    }

    cw.visitEnd();

    return cw.toByteArray();
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
It works if try-catch handler is split into two: the first one covers the code part before the call to super() and the second one covers the code part after the call to super().  This means the call to super() cannot be covered for exceptions.


Comments
As a result of the fix for JDK-8167104 both constructors now get identical results. They both throw VerifyError exceptions.
30-01-2017

Below is the result on different JDK versions == 8uxx - Fail 9 ea b149 - Fail
05-01-2017

The issue is reproducible in all the versions, below is the result. == -sh-4.1$ /opt/java/jdk1.8.0_112/bin/javac X.java -sh-4.1$ /opt/java/jdk1.8.0_112/bin/javac -cp /scratch/fairoz/JI/9046671/asm-5.1/lib/all/asm-all-5.1.jar XDump.java -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java -cp /scratch/fairoz/JI/9046671/asm-5.1/lib/all/asm-all-5.1.jar:. XDump -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java -cp X X.class X.java XDump.class XDump.java -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java -cp X X.class X.java XDump.class XDump.java -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java -cp . X Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.VerifyError: Stack map does not match the one at exception handler 13 Exception Details: Location: X.<init>(LX;)V @13: athrow Reason: Current frame's flags are not assignable to stack map frame's. Current Frame: bci: @0 flags: { flagThisUninit } locals: { uninitializedThis, 'X' } stack: { 'java/lang/Throwable' } Stackmap Frame: bci: @13 flags: { } locals: { top, 'X' } stack: { 'java/lang/Throwable' } Bytecode: 0x0000000: 2abb 0004 5906 b700 09b7 000c b1bf Exception Handler Table: bci [0, 13] => handler: 13 Stackmap Table: full_frame(@13,{Top,Object[#2]},{Object[#14]}) at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.privateGetMethodRecursive(Class.java:3048) at java.lang.Class.getMethod0(Class.java:3018) at java.lang.Class.getMethod(Class.java:1784) at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
05-01-2017