JDK-8026877 : Error in opening JAR file when invalid jar specified with -Xbootclasspath/a on OpenJDK build
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: hs25,8
  • Priority: P5
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2013-10-18
  • Updated: 2013-11-10
  • Resolved: 2013-10-24
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 Other
8Fixed hs25Fixed
Related Reports
Relates :  
Description
/home/roman/prog/jdk/jdk1.8.0/bin/java -Xbootclasspath/a:dummy.jar TestForName
Error occurred during initialization of VM
java/lang/ClassNotFoundException: error in opening JAR file <Zip file open error> dummy.jar

Where dummy.jar is a zero-length file
Comments
test waits exit code 0: JDK-8028095
10-11-2013

A build-time solution turns out to be somewhat cumbersome. There is a simpler way to avoid this difference in behaviour: - there is no reason that PostVMInitHook has to be on the preload list - we can simply do: static void call_postVMInitHook(TRAPS) { Klass* k = SystemDictionary::resolve_or_null(vmSymbols::sun_misc_PostVMInitHook(), THREAD); instanceKlassHandle klass (THREAD, k); if (klass.not_null()) { JavaValue result(T_VOID); JavaCalls::call_static(&result, klass, vmSymbols::run_method_name(), vmSymbols::void_method_signature(), CHECK); } } as we do for loads of other classes in create_vm. This "fixes" the problem.
24-10-2013

We could make all references to PostVMInitHook conditional on doing a non-OPENJDK build, but we'd need to add a "#if USE_POSTVMINITHOOK" and have the closed makefiles set -DUSE_POSTVMINITHOOK=1" for non-OPENJDK. Do you think it worthwhile to do this? It does help avoid the harder issues here.
22-10-2013

Impact - Low: error detected at VM startup where it can be easily corrected Likelihood - high: will always fail this way for OpenJDK builds Workaround - Low: remove the invalid jar file from the bootclasspath. ILW: LHL => P5
22-10-2013

Thanks Bill! I had missed the path to special_exception. Good to see the full picture now. Removing classes from the known classes list will not make any difference. If the class is not in the meta-index then a full search of the bootclasspath will be performed. If it is in the meta-index then we will first search the expected location and if not found then search the rest of the bootclasspath. And note that the meta-index is package-based not class based (and very coarse-grained at that). I'm not convinced this is a bug as such - though at a minimum the test would need to be modified. A bogus jar file should cause an exception - it just happens that in this case this is early enough in VM initialization to cause an abort (which is no different to say throwing at the start of main() - the app terminates either way). Arguably a worse bug is the fact that latter detection of the bad jar file actually gets ignored due to the classloader delegation. Again I think it may have been a mistake to use ClassNotFoundException for this case. I'm not sure how to proceed with this one. For 8 all we can do is modify the test to expect these different failure modes - the test is after all only checking that we don't crash, and I think the test is misguided in assuming that invalid jar is just ignored.
22-10-2013

I ran the release build under gdb and got this stack trace when PostVMInitHook.class is missing: Breakpoint 9, vm_exit_during_initialization (ex=0xe4ed8c38, message=0xf7506a00 "error in opening JAR file <Zip file open error> dummy.jar") at /export/users/bpittore/hotspot-emb/hotspot/src/os/linux/vm/os_linux.inline.hpp:55 55 return pthread_getspecific((pthread_key_t)index); (gdb) bt #0 vm_exit_during_initialization (ex=0xe4ed8c38, message=0xf7506a00 "error in opening JAR file <Zip file open error> dummy.jar") at /export/users/bpittore/hotspot-emb/hotspot/src/os/linux/vm/os_linux.inline.hpp:55 #1 0xf78fbd6d in Exceptions::_throw_msg (thread=0xf7506400, file=0xf7c6bd3c "/export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/classLoader.cpp", line=515, name=0xe4ed8c38, message=0xf7506a00 "error in opening JAR file <Zip file open error> dummy.jar") at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/utilities/exceptions.cpp:104 #2 0xf78775ff in ClassLoader::create_class_path_entry ( path=0xf7508ae0 "dummy.jar", st=0xf75068a8, lazy=0, __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/classLoader.cpp:515 #3 0xf7877e2e in LazyClassPathEntry::open_stream (this=0xf7509088, name=0xf75069e0 "sun/misc/PostVMInitHook.class", __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/classLoader.cpp:304 #4 0xf787956e in ClassLoader::load_classfile (h_name=0xe4ed8840, __the_thread__=0xf7506400) ---Type <return> to continue, or q <return> to quit--- at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/classLoader.cpp:909 #5 0xf7bc556a in SystemDictionary::load_instance_class ( class_name=0xe4ed8840, class_loader=..., __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/systemDictionary.cpp:1301 #6 0xf7bc5e1b in SystemDictionary::resolve_instance_class_or_null ( name=0xe4ed8840, class_loader=..., protection_domain=..., __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/systemDictionary.cpp:779 #7 0xf7bc7361 in SystemDictionary::initialize_wk_klasses_until ( limit_id=WKID_LIMIT, start_id=0xf76bc03c, __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/systemDictionary.cpp:237 #8 0xf7bc755d in SystemDictionary::initialize_preloaded_classes ( __the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/classfile/systemDictionary.cpp:1938 #9 0xf7c0dfab in Universe::genesis (__the_thread__=0xf7506400) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/memory/universe.cpp:287 #10 0xf7c0e26c in universe2_init () ---Type <return> to continue, or q <return> to quit--- at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/memory/universe.cpp:968 #11 0xf796c5c0 in init_globals () at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/runtime/init.cpp:114 #12 0xf7bfd81f in Threads::create_vm (args=0xf76bc338, canTryAgain=0xf76bc2bf) at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/runtime/thread.cpp:3424 #13 0xf79ec583 in JNI_CreateJavaVM () at /export/users/bpittore/hotspot-emb/hotspot/src/share/vm/prims/jni.cpp:5164 #14 0xf7fc736b in ?? () If you look at the stack trace you can see that we get the zip file error in ClassLoader::create_class_path_entry(). This code calls THROW_MSG_() at line 515. This gets us to Exceptions::_throw_msg() which, at line 163, calls special_exception(). This function checks Universe::is_fully_initialized() which is false at this point so we call vm_exit_during_initialization(). The combination of a bogus jar file in the bootclasspath plus a missing well known class on bootclasspath will trigger this. If we remove classes from rt.jar then they should be removed from the well know classes lists. It looks like the exclusion of PostVMInitHook happened in rev 352 of jdk/make/closed/tools/source-bundles/exclude-all in April of 2012 so this bug has probably been lurking since then.
22-10-2013

I have a partial explanation of why we fail but can't quite track the exact path through to the vm_exit_during_initialization. Early in the VM initialization we have this sequence: init_globals() universe2_init() { EXCEPTION_MARK; Universe::genesis(CATCH); SystemDictionary::initialize(CHECK); initialize_preloaded_classes(CHECK); where sun.misc.PostVMInitHook is one of the classes to preload. Because this appears in the meta-index (all sun.* classes do) the classloader code goes straight to rt.jar to try and find it. If it exists, as it normally does for JDK builds, then all is fine and VM initialization continues - note that because of the meta-index none of the class initialization done during VM startup will cause the bootclasspath to be examined, hence the invalid jar file is not seen normally. If sun.misc.PostVMInitHook does not exist in rt.jar - as is the case for an OpenJDK build - then the bootloader commences looking for it. This involves examining the bootclasspath and trying to load the invalid dummy.jar. That results in the exception: java/lang/ClassNotFoundException: error in opening JAR file <Zip file open error> dummy.jar being posted in ClassLoader::create_class_path_entry. As of b107 and the fix for 8024560, this appears to cause NULL to propagate back up and out of genesis - at which point we hit the CATCH macro. But this is where things get confusing: #define CATCH \ THREAD); if (HAS_PENDING_EXCEPTION) { \ oop ex = PENDING_EXCEPTION; \ CLEAR_PENDING_EXCEPTION; \ ex->print(); \ ShouldNotReachHere(); \ } based on this we should abort the VM due to ShouldNotReachHere() and basically crash. But that doesn't happen. Also we know the exception was not cleared, so somehow we have not reached this CATCH logic. The failure mode can be explained if we have somewhere hit an EXCEPTIONMARK. As you know if you hit the ExceptionMark destructor with an exception pending it aborts. But it too has special handling for VM initialization issues: if (is_init_completed()) { exception->print(); fatal("ExceptionMark destructor expects no pending exceptions"); } else { vm_exit_during_initialization(exception); } so we would get the vm_exit_during_initialization, reporting the ClassNotFoundException. But I haven't been able to determine where this EXCEPTIONMARK is. That aside, what happens when the invalid jar is detected later? Well because of normal ClassLoader delegation any attempt to load an application class will be handled first by the boot loader. If it can't find the class it throws ClassNotFoundException and the application classloader then looks for it. So when loading application classes we will always get a ClassNotFoundException thrown and discarded (unless the application classloader also can't find the class). So the fact that the bootloader exception was actually due to the invalid jar file will not been seen. That problem will only be seen if we try to load a class directly using the bootloader, and that class is not in the meta-index. So ... where does this leave us? An OpenJDK build will detect the invalid jar early in VM initialization and cause an early abort. This seems undesirable, but arguably is the right response to this kind of error. In a non-OpenJDK build the VM still detects the problem and tries to report it, but at a time when the application classloader expects to potentially get a ClassNotFoundException. There is no way to tell the difference between "I looked everywhere and couldn't find this class" versus "I ran into an error trying to look for this class". Arguably the latter case should not be reported by the same exception but we can't do anything about that now. That said the test seems to suggest an expectation that this is not an error and will be silently ignored. I don't think the test author appreciated the subtleties of why this is problem sometimes is, and sometimes is not ignored.
22-10-2013

This also affects the SE reference implementation. So any OpenJDK build will have this issue. ri/java-se-8-ri/bin/java -version openjdk version "1.8.0-ea" OpenJDK Runtime Environment (build 1.8.0-ea-b112) OpenJDK 64-Bit Server VM (build 25.0-b54, mixed mode) ri/java-se-8-ri/bin/java -Xbootclasspath/aError occurred during initialization of VM java/lang/ClassNotFoundException: error in opening JAR file <Zip file open error> dummy.jar
21-10-2013

I tested four different configurations and only the RI had different results. 1. Standard SE B111 Linux x86 2. Embedded SE compact1 B111 Linux x86 3. Embedded SE full jre B111 Linux x86 4. Reference Implementation compact1 Linux x86 The RI was the only runtime that produced the error. All other runtimes ignored the bad jar file. ri/jre1.8.0-compact1/bin/java -version openjdk version "1.8.0-ea" OpenJDK Runtime Environment (build 1.8.0-ea-b111, profile compact1) OpenJDK Server VM (build 25.0-b53, mixed mode) ri/jre1.8.0-compact1/bin/java -Xbootclasspath/a:dummy.jar Hello Error occurred during initialization of VM java/lang/ClassNotFoundException: error in opening JAR file <Zip file open error> dummy.jar embedded/ejre1.8.0/bin/java -version java version "1.8.0-ea" Java(TM) SE Embedded Runtime Environment (build 1.8.0-ea-b111, headless) Java HotSpot(TM) Embedded Server VM (build 25.0-b53, mixed mode) embedded/ejre1.8.0/bin/java -Xbootclasspath/a:dummy.jar Hello Hello se/jre1.8.0/bin/java -version java version "1.8.0-ea" Java(TM) SE Runtime Environment (build 1.8.0-ea-b111) Java HotSpot(TM) Server VM (build 25.0-b53, mixed mode) se/jre1.8.0/bin/java -Xbootclasspath/a:dummy.jar Hello Hello embedded/ejre1.8.0-compact1/bin/java -version java version "1.8.0-ea" Java(TM) SE Embedded Runtime Environment (build 1.8.0-ea-b111, profile compact1, headless) Java HotSpot(TM) Embedded Minimal VM (build 25.0-b53, mixed mode) embedded/ejre1.8.0-compact1/bin/java -Xbootclasspath/a:dummy.jar Hello Hello I believe the problem is caused by the fact that the RI is an OpenJDK build which does not contain the sun.misc.PostVMInitHook class. I believe the call to this class is clearing the exception that the VM would otherwise see. If I run with verbose:class, this is the call that appears in a working run right before the OpenJDK build fails.
21-10-2013

Looks like a problem with embedded or profile, as the official JDK's behavior is more preferable.
21-10-2013

I see a difference between behavior of profiles-ri and SE Embedded builds: roman@rmatafon:~/data/tmp$ /home/roman/prog/jdk/jdk8-b111/bin/java -Xbootclasspath/a:dummy.jar TestForName java.lang.ClassNotFoundException: xxx at java.net.URLClassLoader$1.run(URLClassLoader.java:359) at java.net.URLClassLoader$1.run(URLClassLoader.java:348) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:347) at java.lang.ClassLoader.loadClass(ClassLoader.java:423) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) at java.lang.ClassLoader.loadClass(ClassLoader.java:356) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:259) at TestForName.main(TestForName.java:27) roman@rmatafon:~/data/tmp$ /home/roman/prog/jdk/jdk8-b111-ri/bin/java -Xbootclasspath/a:dummy.jar TestForName Error occurred during initialization of VM java/lang/ClassNotFoundException: error in opening JAR file <Zip file open error> dummy.jar roman@rmatafon:~/data/tmp$ /home/roman/prog/jdk/jdk8-b111-ri/bin/java -version openjdk version "1.8.0-ea" OpenJDK Runtime Environment (build 1.8.0-ea-b111) OpenJDK Server VM (build 25.0-b53, mixed mode) roman@rmatafon:~/data/tmp$ /home/roman/prog/jdk/jdk8-b111/bin/java -version java version "1.8.0-ea" Java(TM) SE Runtime Environment (build 1.8.0-ea-b111) Java HotSpot(TM) Server VM (build 25.0-b53, mixed mode) So this should not be closed as "Not an issue"
21-10-2013

Was there anything actually specific to the profile-ri here? The test runtime/LoadClass/LoadClassNegative.java was specifically added to check that the VM fails in this way with an invalid jar file.
21-10-2013

JDK7 actually would crash with a invalid JAR file in bootclasspath (see JDK-8020675), so the JDK8 behavior is actually much more benign.
18-10-2013

This error behavior is not against any specification.
18-10-2013

$ ls -l dummy.jar -rw-r--r-- 1 0 Oct 9 14:10 dummy.jar The JAR file is 0 bytes long, and thus not a valid JAR file. The error message is expected. Although, if you pass dummy.jar on the classpath, it's silently ignored: $ java8 -cp dummy.jar HelloWorld Error: Could not find or load main class HelloWorld $ java8 -cp dummy.jar:. HelloWorld Hello World! I am not sure if the difference in behavior between -Xbootclasspath and -cp is specified. It probably is not. Nevertheless, I would categorize this as a "peculiarity of Java" rather than a bug.
18-10-2013