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

StackMapFrames are missing from redefined class bytes of retransformed classes

XMLWordPrintable

    • b10
    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Reproduced with the latest Java SE versions, 11.0.4 and 8u221, on Ubuntu 16.04.

      A DESCRIPTION OF THE PROBLEM :
      When retransforming a class via JVMTI RetransformClasses, and registering to the ClassFileLoadHook, the original class bytes passed in the classBeingRedefined pointer parameter are missing the StackMapFrame attributes of all methods.

      As a result, when such class is instrumented using ASM, even if no bytes are actually changed, it is likely to crash the JVM. This was originally described in ASM issue 317876: https://gitlab.ow2.org/asm/asm/issues/317876.

      For Java 7+ classes, ASM relies on classes always having a StackMapTable, and attempts to calculate max stack depth and the number of locals using the available StackMaps. When StackMapTable is missing, the max stack is wrongly calculated for the method, resulting in a too small value. Then, when this class is collected during GC, class verification fails (during OopMaps calculation) with the message "fatal error: Illegal class file encountered. Try running with -Xverify:all in method loadProvider", and a hs_err.log will be generated.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      The issue could be reproduced with the following reproducer:

      https://github.com/shaharv/jvm/releases/download/v0.1.asm-317876/jvm-redefined-bytes-missing-stackmaptable.tar.gz

      It consists of a simple JVMTI agent, short java test and test scripts.
      For running the example, extract and ./run.sh.

      The example code is also available in:
      https://github.com/shaharv/jvm/tree/master/jvmti/asm-317876

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The class bytes of redefined and plainly loaded classes both contain StackMapTables.

      The good test output should be as follows:

      ```
      + javap -p -l -v -s -c out/java_util_Date_loaded.class
      + javap -p -l -v -s -c out/java_util_Date_redefined.class
      + grep 'StackMapTable: number_of_entries' out/java_util_Date_loaded.class.disasm.txt
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 53
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 4
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 5
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 1
      + set +e
      ++ grep 'StackMapTable: number_of_entries' out/java_util_Date_redefined.class.disasm.txt
      + FOUND_STACKMAP=' StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 53
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 4
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 5
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 1'
      + set -e
      + '[' -z ' StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 53
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 4
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 5
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 1' ']'
      + echo 'Test passed.'
      Test passed.
      ```
      ACTUAL -
      StackMapTables are missing from the redefined bytes.

      The test output is:

      ```
      + /usr/lib/jvm/jdk-11.0.4/bin/javap -p -l -v -s -c out/java_util_Date_loaded.class
      + /usr/lib/jvm/jdk-11.0.4/bin/javap -p -l -v -s -c out/java_util_Date_redefined.class
      + grep 'StackMapTable: number_of_entries' out/java_util_Date_loaded.class.disasm.txt
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 53
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 4
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 3
            StackMapTable: number_of_entries = 5
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 2
            StackMapTable: number_of_entries = 1
            StackMapTable: number_of_entries = 1
      + set +e
      ++ grep 'StackMapTable: number_of_entries' out/java_util_Date_redefined.class.disasm.txt
      + FOUND_STACKMAP=
      + set -e
      + '[' -z '' ']'
      + echo 'Test failed: StackMapTable not found in redefined bytes.'
      Test failed: StackMapTable not found in redefined bytes.
      ```

      FREQUENCY : always


            amenkov Alex Menkov
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: