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
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
- duplicates
-
JDK-8299798 StackMapFrames missing from classes to be retransformed by ClassFileTransformer
-
- Closed
-