JDK-8263789 : java.lang.ClassFormatError: Illegal field name "1" in class
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 8u281
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2021-03-15
  • Updated: 2023-09-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8-poolUnresolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
OS: Windows 7
Classes compiled with the Microsoft Compiler (jvc, jdk 1.0).
JRE: 8u261 (works), 8u281 (doesn't work).

A DESCRIPTION OF THE PROBLEM :
It would appear there has been some regression in how anonymous inner classes compiled using older JDK's are loaded in 8u281. Previously, using 8u261, this error would not appear. The following stacktrace is given when using 8u281:

java.lang.ClassFormatError: Illegal field name "1" in class postilion/core/tranmgr/PerfMonManager$1
                at java.lang.ClassLoader.defineClass1(Native Method)
                at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
                at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
                at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
                at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
                at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
                at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
                at java.security.AccessController.doPrivileged(Native Method)
                at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
                at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
                at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
                at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

The class in question (PerfMonManager) has a static initialization block that adds a runtime shutdown hook, where the thread defining the shutdown hook is constructed as an anonymous inner class, ie.

Runtime.getRuntime().addShutdownHook(
	new Thread("TranMgr PerfMonManager Cleanup")
		{
			public void run()
			{
				PerfMonManager.close();
			}
		});

This anonymous class is compiled into PerfMonManager$1.class

This is a legacy application that is compiled using the Microsoft Compiler (jvc). My best guess is that the byte-code from these anonymous inner classes, generated using an old JDK, can no longer be understood by the latest JRE. There is no mention in the release notes of anything that would have changed the way classes are loaded.

Unfortunately, this is not the only case where anonymous inner classes are used in our code, and it is not feasible to update the application to not build against the Microsoft Compiler without substantial changes.

Turning off the verifier (-Xverify:none) allows the application to start, but the consequences of this is unknown and definitely not something we can consider doing.

REGRESSION : Last worked in version 8

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Define an anonymous inner class inside a main class, compiled using JVC, and attempt to execute the program using the latest 8u281.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The class should be loaded/executed successfully as in 8u261.
ACTUAL -
java.lang.ClassFormatError: Illegal field name "1" in class is thrown.

FREQUENCY : always



Comments
I've removed all the "Affects Versions" past 8u as this is "not an issue" as the VM is correctly rejecting an invalid identifier. I've targeted this to 8-pool and marked it for sustaining as they may want to provide some kind of mitigation in 8u.
07-05-2021

Workarounds for 8u: 1. Binary edit the classfile to change version 45.3 to 49.0 (whether this suffices depends on the form the other bytecodes and whether they follow version 49 rules). Other post processing tools may be needed eg. disassemble the class file to jcod file, edit file, recomple. 2. Disable verification: -Xverify:none. This is never generally recommended and should only be done if it is known that all classes loaded are trusted. 3. If these classes are in a single jar and are trusted, then add that jar to the bootclasspath, or as a standard extension, so that they no longer get verified by default.
21-04-2021

To summarise the situation: The rejected classfile has version 45.3 and contains a local variable table entry with a name "1". Prior to Java 5 the JVMS states that the name of a local variable (and other names) must be a "simple name" as defined in the JVMS, which in turn is defined as an identifier where "An identifier is an unlimited-length sequence of Unicode letters and digits, the first of which must be a letter." Therefore according to the JVMS rules applicable to classfiles of version 45.3, the rejected classfile is indeed malformed and should be rejected. Prior to the change in JDK-8241481, however, such a class, loaded by the system (aka application) classloader, was not subject to verification and so the name of the local variable was never checked, and so the classfile was (erroneously) accepted. The main aim of JDK-8241481 was to ensure class names were always validated, so the system loader was no longer considered a trusted loader. But this had the additional affect that all names were now verified, and so the erroneous name "1" (under pre-Java 5 rules) was rejected. While the current behaviour is strictly correct, there is a compatibility argument to be made, at least in JDK 8u, that we should continue to allow the class to be accepted (afterall if it had version 49 it would be).
21-04-2021

It looks like that fix went too far in trying to ensure class names loaded by the app loader are format checked. It resulted in the field (and other) names also being format checked. Which now involves the (apparently incorrect) skip_over_field_name.
20-04-2021

Found it. The notion of trusted classloader was changed. Previously the system loader was trusted, but after JDK-8241481 only the boot and extensions loader. New code: bool trusted = (loader_data->is_the_null_class_loader_data() || SystemDictionary::is_ext_class_loader(loader_data->class_loader())); Old code: bool trusted = java_lang_ClassLoader::is_trusted_loader(loader); bool java_lang_ClassLoader::is_trusted_loader(oop loader) { // Fix for 4474172; see evaluation for more details loader = non_reflection_class_loader(loader); oop cl = SystemDictionary::java_system_loader(); while(cl != NULL) { if (cl == loader) return true; cl = parent(cl); } return false; }
20-04-2021

Change is in 8u281, code is loaded fine in 8u271. Starts failing in b01. I have to again suspect JDK-8241481 but couldn't previously spot the trigger.
20-04-2021

I just spotted the issue: LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Ljrebug/AnonInnerClass$1; 0 6 1 1 Ljava/lang/String; the classfile appears to have a local variable named "1". That said I'm still not clear why this is flagged as an error. The name of a local variable in the LVT need only be a valid unqualified name [JVMS 4.2.2] and they are not constrained to start with a non-numeric value. The Java language constrains identifiers in this way [JLS 3.8] but the JVM does not. I found the code that is wrong: skip_over_field_name checks the Java language rules for field names, hence it will return NULL and cause verify_field_name to fail. I cannot see what change may have caused this to start failing yet, but will dig further.
20-04-2021

This is very curious. Here is the javap output for the class file: Classfile /scratch/users/daholme/jrebug/AnonInnerClass$1.class Last modified Apr 20, 2021; size 696 bytes SHA-256 checksum 0f0d57c9279175421bbd91f840ec40981ff27286224ac70490073353b29b6873 Compiled from "AnonInnerClass.java" class jrebug.AnonInnerClass$1 extends java.lang.Thread minor version: 3 major version: 45 flags: (0x0020) ACC_SUPER this_class: #10 // jrebug/AnonInnerClass$1 super_class: #12 // java/lang/Thread interfaces: 0, fields: 0, methods: 2, attributes: 3 Constant pool: #1 = Utf8 Code #2 = Utf8 SourceFile #3 = Utf8 SourceDir #4 = Utf8 ConstantValue #5 = Utf8 Exceptions #6 = Utf8 LineNumberTable #7 = Utf8 LocalVariableTable #8 = Utf8 InnerClasses #9 = Utf8 jrebug/AnonInnerClass$1 #10 = Class #9 // jrebug/AnonInnerClass$1 #11 = Utf8 java/lang/Thread #12 = Class #11 // java/lang/Thread #13 = Utf8 (Ljava/lang/String;)V #14 = Utf8 <init> #15 = NameAndType #14:#13 // "<init>":(Ljava/lang/String;)V #16 = Methodref #12.#15 // java/lang/Thread."<init>":(Ljava/lang/String;)V #17 = Utf8 this #18 = Utf8 Ljrebug/AnonInnerClass$1; #19 = Utf8 1 #20 = Utf8 Ljava/lang/String; #21 = Utf8 Ljava/io/PrintStream; #22 = Utf8 out #23 = NameAndType #22:#21 // out:Ljava/io/PrintStream; #24 = Utf8 java/lang/System #25 = Class #24 // java/lang/System #26 = Fieldref #25.#23 // java/lang/System.out:Ljava/io/PrintStream; #27 = Utf8 Hello #28 = String #27 // Hello #29 = Utf8 println #30 = NameAndType #29:#13 // println:(Ljava/lang/String;)V #31 = Utf8 java/io/PrintStream #32 = Class #31 // java/io/PrintStream #33 = Methodref #32.#30 // java/io/PrintStream.println:(Ljava/lang/String;)V #34 = Utf8 AnonInnerClass.java #35 = Utf8 C:\\Temp\\jre_issue\\src\\jrebug\\ #36 = Utf8 run #37 = Utf8 ()V #38 = Utf8 java/lang/InterruptedException #39 = Class #38 // java/lang/InterruptedException { jrebug.AnonInnerClass$1(java.lang.String) throws java.lang.InterruptedException; descriptor: (Ljava/lang/String;)V flags: (0x0000) Exceptions: throws java.lang.InterruptedException Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: invokespecial #16 // Method java/lang/Thread."<init>":(Ljava/lang/String;)V 5: return LineNumberTable: line 22: 1 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Ljrebug/AnonInnerClass$1; 0 6 1 1 Ljava/lang/String; public void run(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #28 // String Hello 5: invokevirtual #33 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 12: 0 line 13: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Ljrebug/AnonInnerClass$1; } SourceFile: "AnonInnerClass.java" SourceDir: length = 0x2 (unknown attribute) 00 23 InnerClasses: #10; // class jrebug/AnonInnerClass$1 We can see constant pool entry #19 is the utf8 string "1", but there are no other references to that CP entry, nr are there any fields declared yet when loading the class in the proper VM we get: Error: LinkageError occurred while loading main class jrebug.AnonInnerClass$1 java.lang.ClassFormatError: Illegal field name "1" in class jrebug/AnonInnerClass$1 Logging is uninformative so I will have to dig deeper into what is happening.
20-04-2021

Additional Information from submitter: =========================== <ATTACHED><jre_bug_example.zip>
20-04-2021

Mail to Submitter: ============= Can you please share the .class file generated by Microsoft compiler?
09-04-2021

On further investigation JDK-8148854 seems to have moved the code aorund but not actually change the verification requirement.
09-04-2021

Can the submitter provide the .class file generated by the Microsoft compiler? It is extremely likely that it is generating an invalid class file that the VM will no longer tolerate (unless verification is disabled).
09-04-2021

Checked with attached test in Windows 10, in 8u281, 8u271, didn't throw any exception, I was able to print hello attached screenshot for reference<TestCapture.PNG>
08-04-2021

Additional information from submitter: =========================== Please find attached a very simple class that demonstrates the error. The issue seems limited to a method whose parameter is a thread. The classes were compiled using the Microsoft Compiler (jvc). When run against Java 8u281, the following error is thrown: <ATTACHED><SCREENSHOT><Capture1.PNG> When run against java 8u281 (or earlier), the following is displayed: <ATTACHED><SCREENSHOT><Capture2.PNG> It’s interesting it’s limited to method calls who have a parameter for Thread. I’ve tried a few other objects and none seem to cause the error.
08-04-2021

Not received any additional information from submitter, closing this report as incomplete. Shall reopen this report if any updates received
05-04-2021

This is not at all related to JavaFX, so I changed to hotspot/runtime (not sure if that is the right place, either).
01-04-2021

Mail to submitter: ============= Could you please share minimal reproducible testcase to analyze the issue better at our end.
18-03-2021