Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8365179

1.8.0_451 Incorrectly Misses ClassFormatError for Invalid clinit

XMLWordPrintable

    • x86_64
    • windows

      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 ----------

            dholmes David Holmes
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: