ADDITIONAL SYSTEM INFORMATION :
ADDITIONAL SYSTEM INFORMATION :
```
OS:
Operating System Name: Windows 11
Operating System Architecture: amd64
Operating System Version: 10.0
```
OpenJDK version:
```
java version "1.8.0_451"
Java(TM) SE Runtime Environment (build 1.8.0_451-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.451-b10, mixed mode)
java version "11.0.27" 2025-04-15 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.27+8-LTS-232)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.27+8-LTS-232, mixed mode)
```
Output from `java -version`.
A DESCRIPTION OF THE PROBLEM :
Given a test case, we found that the execution results of this test case on different versions are different, the simplified test case can be found below. In summary, 11.0.27 threw
`ClassFormatError` while 1.8.0_451 threw `VerifyError`.
If the JVM fails to properly validate the bytecode, it could easily lead to the execution of incorrect files, thereby introducing potential risks.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Repoduce:
```
>>> path_to_jdk/hotspot/1.8.0_451/bin/java -cp . TestLauncher
>>> path_to_jdk/hotspot/11.0.27/bin/java -cp . TestLauncher
```
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Based on the results above, we consulted the JVM SE8 specification, and in [JVM SE8 Chapter 2.9](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9), we found the following statement: "In a `class` file whose version number is 51.0 or above, the method must additionally have its `ACC_STATIC` flag ([§4.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6)) set in order to be the class or interface initialization method."
Subsequently, we disassembled `OuterClass1$InnerClass1` using `javap` and discovered that the class file version is major version 52. Therefore, the test case should throw a `ClassFormatError` rather than running `OuterClass1` successfully.
ACTUAL -
The output is as follows:
hotspot(1.8.0_451):
```
123
Exception in thread "main" java.lang.VerifyError: Operand stack underflow
Exception Details:
Location:
OuterClass2.run()V @11: invokevirtual
Reason:
Attempt to pop empty stack.
Current Frame:
bci: @11
flags: { }
locals: { }
stack: { integer }
Bytecode:
0x0000000: b200 0c12 0eb6 0014 b200 1ab6 001e b800
0x0000010: 21b2 000c 1223 b600 14b2 001a b600 1eb1
0x0000020:
at TestLauncher.main(Unknown Source)
```
hotspot(11.0.27):
```
Exception in thread "main" java.lang.ClassFormatError: Method <clinit> is not static in class file OuterClass1$InnerClass1
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1021)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at OuterClass1.run(Unknown Source)
at TestLauncher.main(Unknown Source)
```
---------- BEGIN SOURCE ----------
The following code can be used to generate the test cases(.class file) that reproduces the above process:
```java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class BytecodeUtil {
public static void main(String args[]) {
// create package
String baseDir = "./your_dir"; // your base dir
File baseDirFile = new File(baseDir);
baseDirFile.mkdirs();
String OuterClass1_bytecodeInStr = "CAFEBABE00000034001A01000B4F75746572436C617373310700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B0100174F75746572436C6173733124496E6E6572436C6173733107000D01000B7374617469634669656C64010001490C000F001009000E00110100136A6176612F696F2F5072696E7453747265616D0700130100077072696E746C6E010004284929560C001500160A00140017010004436F64650021000200040000000000010009000500060001001900000016000200000000000AB2000CB20012B60018B1000000000000" ;
String OuterClass1$InnerClass1_bytecodeInStr = "CAFEBABE00000034000C0100174F75746572436C6173733124496E6E6572436C617373310700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C6401000149030000007B0100083C636C696E69743E01000328295601000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000A00000002000700010001000800090001000B0000000D0000000100000001B1000000000000" ;
String OuterClass2_bytecodeInStr = "CAFEBABE00000034002501000B4F75746572436C617373320700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B0100154265666F7265206D6F64696669636174696F6E3A2008000D0100136A6176612F696F2F5072696E7453747265616D07000F0100057072696E74010015284C6A6176612F6C616E672F537472696E673B29560C001100120A001000130100174F75746572436C6173733224496E6E6572436C6173733207001501000B7374617469634669656C64010001490C0017001809001600190100077072696E746C6E010004284929560C001B001C0A0010001D0100116D6F646966795374617469634669656C640C001F00060A001600200100144166746572206D6F64696669636174696F6E3A20080022010004436F6465002100020004000000000001000900050006000100240000002C0002000000000020B2000C120EB60014B2001AB6001EB80021B2000C1223B60014B2001AB6001EB1000000000000" ;
String OuterClass2$InnerClass2_bytecodeInStr = "CAFEBABE00000034000E0100174F75746572436C6173733224496E6E6572436C617373320700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C640100014903000001C80100116D6F646966795374617469634669656C640100032829560C00050006090002000A01000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000C00000002000700010009000800090001000D000000150002000000000009B2000B0860B5000BB1000000000000" ;
String OuterClass3_bytecodeInStr = "CAFEBABE00000034001601000B4F75746572436C617373330700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B01002A56616C696420636F64652C20496E6E6572436C617373332077696C6C206661696C20746F206C6F61642E08000D0100136A6176612F696F2F5072696E7453747265616D07000F0100077072696E746C6E010015284C6A6176612F6C616E672F537472696E673B29560C001100120A00100013010004436F646500210002000400000000000100090005000600010015000000150002000000000009B2000C120EB60014B1000000000000" ;
String OuterClass3$InnerClass3_bytecodeInStr = "CAFEBABE00000034000E0100174F75746572436C6173733324496E6E6572436C617373330700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C640100014903000003150100083C636C696E69743E0100032829560C00050006090002000A01000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000C00000002000700010008000800090001000D000000150002000000000009B2000B0460B5000BB1000000000000" ;
String TestLauncher_bytecodeInStr = "CAFEBABE00000034001401000C546573744C61756E636865720700010100106A6176612F6C616E672F4F626A6563740700030100046D61696E010016285B4C6A6176612F6C616E672F537472696E673B295601000B4F75746572436C6173733107000701000372756E0100032829560C0009000A0A0008000B01000B4F75746572436C6173733207000D0A000E000B01000B4F75746572436C617373330700100A0011000B010004436F64650021000200040000000000010009000500060001001300000016000000010000000AB8000CB8000FB80012B1000000000000" ;
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass1.class")) {
byte[] OuterClass1_bytecode = hexStringToByteArray(OuterClass1_bytecodeInStr);
fos.write(OuterClass1_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass1$InnerClass1.class")) {
byte[] OuterClass1$InnerClass1_bytecode = hexStringToByteArray(OuterClass1$InnerClass1_bytecodeInStr);
fos.write(OuterClass1$InnerClass1_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass2.class")) {
byte[] OuterClass2_bytecode = hexStringToByteArray(OuterClass2_bytecodeInStr);
fos.write(OuterClass2_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass2$InnerClass2.class")) {
byte[] OuterClass2$InnerClass2_bytecode = hexStringToByteArray(OuterClass2$InnerClass2_bytecodeInStr);
fos.write(OuterClass2$InnerClass2_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass3.class")) {
byte[] OuterClass3_bytecode = hexStringToByteArray(OuterClass3_bytecodeInStr);
fos.write(OuterClass3_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "TestLauncher.class")) {
byte[] TestLauncher_bytecode = hexStringToByteArray(TestLauncher_bytecodeInStr);
fos.write(TestLauncher_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] byteArray = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
int byteValue = Integer.parseInt(hexString.substring(i, i + 2), 16);
byteArray[i / 2] = (byte) byteValue;
}
return byteArray;
}
}
```
Note: modify the paths according to your needs.
After obtaining the test cases, you can derive the following test cases through decompilation:
```java
public class OuterClass1 {
public static void run() {
System.out.println(OuterClass1$InnerClass1.staticField);
}
}
```
```java
public static class OuterClass1$InnerClass1 {
public static int staticField;
}
```
```java
public class OuterClass2 {
public static void run() {
// $FF: Couldn't be decompiled
}
}
```
```java
public static class OuterClass2$InnerClass2 {
public static int staticField;
public static void modifyStaticField() {
// $FF: Couldn't be decompiled
}
}
```
```java
public class OuterClass3 {
public static void run() {
System.out.println("Valid code, InnerClass3 will fail to load.");
}
}
```
```java
public static class OuterClass3$InnerClass3 {
public static int staticField;
static {
// $FF: Couldn't be decompiled
}
}
```
```java
public class TestLauncher {
public static void main(String[] var0) {
OuterClass1.run();
OuterClass2.run();
OuterClass3.run();
}
}
```
---------- END SOURCE ----------
ADDITIONAL SYSTEM INFORMATION :
```
OS:
Operating System Name: Windows 11
Operating System Architecture: amd64
Operating System Version: 10.0
```
OpenJDK version:
```
java version "1.8.0_451"
Java(TM) SE Runtime Environment (build 1.8.0_451-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.451-b10, mixed mode)
java version "11.0.27" 2025-04-15 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.27+8-LTS-232)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.27+8-LTS-232, mixed mode)
```
Output from `java -version`.
A DESCRIPTION OF THE PROBLEM :
Given a test case, we found that the execution results of this test case on different versions are different, the simplified test case can be found below. In summary, 11.0.27 threw
`ClassFormatError` while 1.8.0_451 threw `VerifyError`.
If the JVM fails to properly validate the bytecode, it could easily lead to the execution of incorrect files, thereby introducing potential risks.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Repoduce:
```
>>> path_to_jdk/hotspot/1.8.0_451/bin/java -cp . TestLauncher
>>> path_to_jdk/hotspot/11.0.27/bin/java -cp . TestLauncher
```
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Based on the results above, we consulted the JVM SE8 specification, and in [JVM SE8 Chapter 2.9](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9), we found the following statement: "In a `class` file whose version number is 51.0 or above, the method must additionally have its `ACC_STATIC` flag ([§4.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6)) set in order to be the class or interface initialization method."
Subsequently, we disassembled `OuterClass1$InnerClass1` using `javap` and discovered that the class file version is major version 52. Therefore, the test case should throw a `ClassFormatError` rather than running `OuterClass1` successfully.
ACTUAL -
The output is as follows:
hotspot(1.8.0_451):
```
123
Exception in thread "main" java.lang.VerifyError: Operand stack underflow
Exception Details:
Location:
OuterClass2.run()V @11: invokevirtual
Reason:
Attempt to pop empty stack.
Current Frame:
bci: @11
flags: { }
locals: { }
stack: { integer }
Bytecode:
0x0000000: b200 0c12 0eb6 0014 b200 1ab6 001e b800
0x0000010: 21b2 000c 1223 b600 14b2 001a b600 1eb1
0x0000020:
at TestLauncher.main(Unknown Source)
```
hotspot(11.0.27):
```
Exception in thread "main" java.lang.ClassFormatError: Method <clinit> is not static in class file OuterClass1$InnerClass1
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1021)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at OuterClass1.run(Unknown Source)
at TestLauncher.main(Unknown Source)
```
---------- BEGIN SOURCE ----------
The following code can be used to generate the test cases(.class file) that reproduces the above process:
```java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class BytecodeUtil {
public static void main(String args[]) {
// create package
String baseDir = "./your_dir"; // your base dir
File baseDirFile = new File(baseDir);
baseDirFile.mkdirs();
String OuterClass1_bytecodeInStr = "CAFEBABE00000034001A01000B4F75746572436C617373310700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B0100174F75746572436C6173733124496E6E6572436C6173733107000D01000B7374617469634669656C64010001490C000F001009000E00110100136A6176612F696F2F5072696E7453747265616D0700130100077072696E746C6E010004284929560C001500160A00140017010004436F64650021000200040000000000010009000500060001001900000016000200000000000AB2000CB20012B60018B1000000000000" ;
String OuterClass1$InnerClass1_bytecodeInStr = "CAFEBABE00000034000C0100174F75746572436C6173733124496E6E6572436C617373310700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C6401000149030000007B0100083C636C696E69743E01000328295601000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000A00000002000700010001000800090001000B0000000D0000000100000001B1000000000000" ;
String OuterClass2_bytecodeInStr = "CAFEBABE00000034002501000B4F75746572436C617373320700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B0100154265666F7265206D6F64696669636174696F6E3A2008000D0100136A6176612F696F2F5072696E7453747265616D07000F0100057072696E74010015284C6A6176612F6C616E672F537472696E673B29560C001100120A001000130100174F75746572436C6173733224496E6E6572436C6173733207001501000B7374617469634669656C64010001490C0017001809001600190100077072696E746C6E010004284929560C001B001C0A0010001D0100116D6F646966795374617469634669656C640C001F00060A001600200100144166746572206D6F64696669636174696F6E3A20080022010004436F6465002100020004000000000001000900050006000100240000002C0002000000000020B2000C120EB60014B2001AB6001EB80021B2000C1223B60014B2001AB6001EB1000000000000" ;
String OuterClass2$InnerClass2_bytecodeInStr = "CAFEBABE00000034000E0100174F75746572436C6173733224496E6E6572436C617373320700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C640100014903000001C80100116D6F646966795374617469634669656C640100032829560C00050006090002000A01000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000C00000002000700010009000800090001000D000000150002000000000009B2000B0860B5000BB1000000000000" ;
String OuterClass3_bytecodeInStr = "CAFEBABE00000034001601000B4F75746572436C617373330700010100106A6176612F6C616E672F4F626A65637407000301000372756E0100032829560100106A6176612F6C616E672F53797374656D0700070100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0C0009000A090008000B01002A56616C696420636F64652C20496E6E6572436C617373332077696C6C206661696C20746F206C6F61642E08000D0100136A6176612F696F2F5072696E7453747265616D07000F0100077072696E746C6E010015284C6A6176612F6C616E672F537472696E673B29560C001100120A00100013010004436F646500210002000400000000000100090005000600010015000000150002000000000009B2000C120EB60014B1000000000000" ;
String OuterClass3$InnerClass3_bytecodeInStr = "CAFEBABE00000034000E0100174F75746572436C6173733324496E6E6572436C617373330700010100106A6176612F6C616E672F4F626A65637407000301000B7374617469634669656C640100014903000003150100083C636C696E69743E0100032829560C00050006090002000A01000D436F6E7374616E7456616C7565010004436F6465000900020004000000010009000500060001000C00000002000700010008000800090001000D000000150002000000000009B2000B0460B5000BB1000000000000" ;
String TestLauncher_bytecodeInStr = "CAFEBABE00000034001401000C546573744C61756E636865720700010100106A6176612F6C616E672F4F626A6563740700030100046D61696E010016285B4C6A6176612F6C616E672F537472696E673B295601000B4F75746572436C6173733107000701000372756E0100032829560C0009000A0A0008000B01000B4F75746572436C6173733207000D0A000E000B01000B4F75746572436C617373330700100A0011000B010004436F64650021000200040000000000010009000500060001001300000016000000010000000AB8000CB8000FB80012B1000000000000" ;
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass1.class")) {
byte[] OuterClass1_bytecode = hexStringToByteArray(OuterClass1_bytecodeInStr);
fos.write(OuterClass1_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass1$InnerClass1.class")) {
byte[] OuterClass1$InnerClass1_bytecode = hexStringToByteArray(OuterClass1$InnerClass1_bytecodeInStr);
fos.write(OuterClass1$InnerClass1_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass2.class")) {
byte[] OuterClass2_bytecode = hexStringToByteArray(OuterClass2_bytecodeInStr);
fos.write(OuterClass2_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass2$InnerClass2.class")) {
byte[] OuterClass2$InnerClass2_bytecode = hexStringToByteArray(OuterClass2$InnerClass2_bytecodeInStr);
fos.write(OuterClass2$InnerClass2_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "OuterClass3.class")) {
byte[] OuterClass3_bytecode = hexStringToByteArray(OuterClass3_bytecodeInStr);
fos.write(OuterClass3_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
try (FileOutputStream fos = new FileOutputStream(baseDir + "/" + "TestLauncher.class")) {
byte[] TestLauncher_bytecode = hexStringToByteArray(TestLauncher_bytecodeInStr);
fos.write(TestLauncher_bytecode);
} catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] byteArray = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
int byteValue = Integer.parseInt(hexString.substring(i, i + 2), 16);
byteArray[i / 2] = (byte) byteValue;
}
return byteArray;
}
}
```
Note: modify the paths according to your needs.
After obtaining the test cases, you can derive the following test cases through decompilation:
```java
public class OuterClass1 {
public static void run() {
System.out.println(OuterClass1$InnerClass1.staticField);
}
}
```
```java
public static class OuterClass1$InnerClass1 {
public static int staticField;
}
```
```java
public class OuterClass2 {
public static void run() {
// $FF: Couldn't be decompiled
}
}
```
```java
public static class OuterClass2$InnerClass2 {
public static int staticField;
public static void modifyStaticField() {
// $FF: Couldn't be decompiled
}
}
```
```java
public class OuterClass3 {
public static void run() {
System.out.println("Valid code, InnerClass3 will fail to load.");
}
}
```
```java
public static class OuterClass3$InnerClass3 {
public static int staticField;
static {
// $FF: Couldn't be decompiled
}
}
```
```java
public class TestLauncher {
public static void main(String[] var0) {
OuterClass1.run();
OuterClass2.run();
OuterClass3.run();
}
}
```
---------- END SOURCE ----------