-
Bug
-
Resolution: Fixed
-
P4
-
22
-
b25
-
Verified
The ClassFile API can be instructed to create an invalid code shape, which by itself is not problematic, but the StakMap generator will accept the code, and generate some (obviously wrong) Stack Map for it.
The test is:
---
/*
* @test
* @summary Testing Classfile stack maps generator.
* @build testdata.*
* @run junit WrongInputStackMapTest
*/
import jdk.internal.classfile.*;
import org.junit.jupiter.api.Test;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
/**
* StackMapsTest
*/
class WrongInputStackMapTest {
@Test
void testX() throws Exception {
var cc = Classfile.of();
byte[] data = cc.build(ClassDesc.of("Test"), clb -> {
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
.withMethodBody("test",
MethodTypeDesc.ofDescriptor("(Z)V"),
Classfile.ACC_STATIC,
cb -> {
Label target = cb.newLabel();
Label next = cb.newLabel();
cb.iload(0);
cb.ifeq(next);
cb.constantInstruction(0.0d);
cb.goto_(target);
cb.labelBinding(next);
cb.constantInstruction(0);
cb.labelBinding(target);
cb.pop();
});
});
java.lang.invoke.MethodHandles.lookup().defineHiddenClass(data, true);
}
}
---
This fails with:
---
STARTED WrongInputStackMapTest::testX 'testX()'
java.lang.VerifyError: Inconsistent stackmap frames at branch target 9
Exception Details:
Location:
Test+0x000000007e18cc00.test(Z)V @5: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @5
flags: { }
locals: { integer }
stack: { double, double_2nd }
Stackmap Frame:
bci: @9
flags: { }
locals: { integer }
stack: { }
Bytecode:
0000000: 1a99 0007 0ea7 0004 0357
Stackmap Table:
same_frame(@8)
same_frame(@9)
at java.base/java.lang.ClassLoader.defineClass0(Native Method)
at java.base/java.lang.System$2.defineClass(System.java:2394)
at java.base/java.lang.invoke.MethodHandles$Lookup.defineHiddenClass(MethodHandles.java:2137)
at WrongInputStackMapTest.testX(WrongInputStackMapTest.java:64)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.lang.Thread.run(Thread.java:1570)
FAILED WrongInputStackMapTest::testX 'testX()'
---
While it should preferably fail before that, while generating the StackMaps. The resulting classfile is:
---
Classfile /tmp/stackmap-test.class
Last modified Nov 16, 2023; size 139 bytes
SHA-256 checksum 0b3e034baeac4553c9ba9b0c958288689ba9f2b9bce1ff8feefe1326c5aae553
final class Test
minor version: 0
major version: 66
flags: (0x1030) ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
this_class: #2 // Test
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 0
Constant pool:
#1 = Utf8 Test
#2 = Class #1 // Test
#3 = Utf8 test
#4 = Utf8 (Z)V
#5 = Utf8 java/lang/Object
#6 = Class #5 // java/lang/Object
#7 = Utf8 Code
#8 = Utf8 StackMapTable
{
static void test(boolean);
descriptor: (Z)V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: iload_0
1: ifeq 8
4: dconst_0
5: goto 9
8: iconst_0
9: pop
StackMapTable: number_of_entries = 2
frame_type = 8 /* same */
frame_type = 0 /* same */
}
---
The test is:
---
/*
* @test
* @summary Testing Classfile stack maps generator.
* @build testdata.*
* @run junit WrongInputStackMapTest
*/
import jdk.internal.classfile.*;
import org.junit.jupiter.api.Test;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
/**
* StackMapsTest
*/
class WrongInputStackMapTest {
@Test
void testX() throws Exception {
var cc = Classfile.of();
byte[] data = cc.build(ClassDesc.of("Test"), clb -> {
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
.withMethodBody("test",
MethodTypeDesc.ofDescriptor("(Z)V"),
Classfile.ACC_STATIC,
cb -> {
Label target = cb.newLabel();
Label next = cb.newLabel();
cb.iload(0);
cb.ifeq(next);
cb.constantInstruction(0.0d);
cb.goto_(target);
cb.labelBinding(next);
cb.constantInstruction(0);
cb.labelBinding(target);
cb.pop();
});
});
java.lang.invoke.MethodHandles.lookup().defineHiddenClass(data, true);
}
}
---
This fails with:
---
STARTED WrongInputStackMapTest::testX 'testX()'
java.lang.VerifyError: Inconsistent stackmap frames at branch target 9
Exception Details:
Location:
Test+0x000000007e18cc00.test(Z)V @5: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @5
flags: { }
locals: { integer }
stack: { double, double_2nd }
Stackmap Frame:
bci: @9
flags: { }
locals: { integer }
stack: { }
Bytecode:
0000000: 1a99 0007 0ea7 0004 0357
Stackmap Table:
same_frame(@8)
same_frame(@9)
at java.base/java.lang.ClassLoader.defineClass0(Native Method)
at java.base/java.lang.System$2.defineClass(System.java:2394)
at java.base/java.lang.invoke.MethodHandles$Lookup.defineHiddenClass(MethodHandles.java:2137)
at WrongInputStackMapTest.testX(WrongInputStackMapTest.java:64)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.lang.Thread.run(Thread.java:1570)
FAILED WrongInputStackMapTest::testX 'testX()'
---
While it should preferably fail before that, while generating the StackMaps. The resulting classfile is:
---
Classfile /tmp/stackmap-test.class
Last modified Nov 16, 2023; size 139 bytes
SHA-256 checksum 0b3e034baeac4553c9ba9b0c958288689ba9f2b9bce1ff8feefe1326c5aae553
final class Test
minor version: 0
major version: 66
flags: (0x1030) ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
this_class: #2 // Test
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 0
Constant pool:
#1 = Utf8 Test
#2 = Class #1 // Test
#3 = Utf8 test
#4 = Utf8 (Z)V
#5 = Utf8 java/lang/Object
#6 = Class #5 // java/lang/Object
#7 = Utf8 Code
#8 = Utf8 StackMapTable
{
static void test(boolean);
descriptor: (Z)V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: iload_0
1: ifeq 8
4: dconst_0
5: goto 9
8: iconst_0
9: pop
StackMapTable: number_of_entries = 2
frame_type = 8 /* same */
frame_type = 0 /* same */
}
---