Skip to content

Instantly share code, notes, and snippets.

@marchof
Created September 20, 2017 17:54
Show Gist options
  • Save marchof/8d2047c34b512acfaac09928d4b59d35 to your computer and use it in GitHub Desktop.
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
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