Created
September 20, 2017 17:54
-
-
Save marchof/8d2047c34b512acfaac09928d4b59d35 to your computer and use it in GitHub Desktop.
BugReport for ASM: AnalyzerAdapter calculates wrong type for AALOAD in case of Null
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
According to JVMS Chapter "4.10.1.9 Type Checking Instructions, AALOAD", the resulting type on the operand stack is | |
1) the component type if the input type is an array | |
2) or null if the input type is null | |
This is how the JVM verifier is implemented -- even if the latter will always lead to a NPE at runtime. | |
AnalyzerAdapter handles the second case wrong. | |
STEPS TO REPRODUCE | |
1) Visit frame with operand stack [null, int] | |
2) Visit AALOAD | |
EXPECTED BEHAVIOR | |
Operand Stack is [null] | |
ACTUAL BEHAVIOR | |
Operand Stack is ["java/lang/Object"] | |
DETAILED ANALYSIS | |
Ome might think this situation can be easily produces by the following snippet: | |
Object[] array = null; | |
Object element = array[0]; | |
// some control structure enforcing a frame here | |
Java compilers (e.g. JDK or ECJ) insert a frame with local variables of type | |
["[Ljava/lang/Object;", "java/lang/Object"] | |
If the same class files has been written by ASM with the COMPUTE_FRAME flag ASM | |
assumes "null" for the variables, so the frame is | |
[null, null] | |
Both frame veriants pass the JVM's bytecode verifier. | |
If AnalyzerAdapter is used to track the frame status for the first version the correct | |
frame types are calculated, but for the second version | |
[null, "java/lang/Object"] | |
is calculated which fails the JVM's verifier if inserted like this. A typical error message is: | |
java.lang.VerifyError: Instruction type does not match stack map | |
Exception Details: | |
Location: | |
org/jacoco/dev/bytecode/AALOADnullTest$Target.run()V @39: aload_1 | |
Reason: | |
Type 'java/lang/Object' (current frame, locals[3]) is not assignable to null (stack map, locals[3]) | |
Current Frame: | |
bci: @39 | |
flags: { } | |
locals: { 'org/jacoco/dev/bytecode/AALOADnullTest$Target', '[Z', null, 'java/lang/Object' } | |
stack: { } | |
Stackmap Frame: | |
bci: @39 | |
flags: { } | |
locals: { 'org/jacoco/dev/bytecode/AALOADnullTest$Target', '[Z', null, null } | |
stack: { } | |
AnalyzerAdapter can be fixed by replacing | |
case Opcodes.AALOAD: | |
pop(1); | |
t1 = pop(); | |
if (t1 instanceof String) { | |
pushDesc(((String) t1).substring(1)); | |
} else { | |
push("java/lang/Object"); | |
} | |
with | |
case Opcodes.AALOAD: | |
pop(1); | |
t1 = pop(); | |
if (t1 instanceof String) { | |
pushDesc(((String) t1).substring(1)); | |
} else { | |
push(Opcodes.NULL); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment