Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8172282

Inconsistent class verification of initializers

    Details

      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.

        Attachments

          Activity

            People

            • Assignee:
              hseigel Harold Seigel
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: