Skip to content

Commit 3f27a03

Browse files
committed
8368727: CDS custom loader support causes asserts during class unloading
Reviewed-by: coleenp, dholmes
1 parent 1d55ade commit 3f27a03

File tree

5 files changed

+61
-12
lines changed

5 files changed

+61
-12
lines changed

src/hotspot/share/classfile/systemDictionaryShared.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
174174

175175
// No longer holding SharedDictionary_lock
176176
// No need to lock, as <ik> can be held only by a single thread.
177-
loader_data->add_class(ik);
178177

179178
// Get the package entry.
180179
PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader);

src/hotspot/share/oops/klass.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -872,11 +872,10 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
872872
// modify the CLD list outside a safepoint.
873873
if (class_loader_data() == nullptr) {
874874
set_class_loader_data(loader_data);
875-
876-
// Add to class loader list first before creating the mirror
877-
// (same order as class file parsing)
878-
loader_data->add_class(this);
879875
}
876+
// Add to class loader list first before creating the mirror
877+
// (same order as class file parsing)
878+
loader_data->add_class(this);
880879

881880
Handle loader(THREAD, loader_data->class_loader());
882881
ModuleEntry* module_entry = nullptr;

test/hotspot/jtreg/runtime/cds/appcds/customLoader/UnloadUnregisteredLoaderTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,9 +29,10 @@
2929
* @requires vm.cds
3030
* @requires vm.cds.custom.loaders
3131
* @requires vm.opt.final.ClassUnloading
32-
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
32+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
3333
* @build jdk.test.whitebox.WhiteBox jdk.test.lib.classloader.ClassUnloadCommon
3434
* @compile test-classes/UnloadUnregisteredLoader.java test-classes/CustomLoadee.java
35+
* test-classes/CustomLoadee5.java test-classes/CustomLoadee5Child.java
3536
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
3637
* jdk.test.lib.classloader.ClassUnloadCommon
3738
* jdk.test.lib.classloader.ClassUnloadCommon$1
@@ -49,12 +50,19 @@ public class UnloadUnregisteredLoaderTest {
4950
CDSOptions.disableRuntimePrefixForEpsilonGC();
5051
}
5152
public static void main(String[] args) throws Exception {
52-
String appJar1 = JarBuilder.build("UnloadUnregisteredLoader_app1", "UnloadUnregisteredLoader");
53+
String appJar1 = JarBuilder.build("UnloadUnregisteredLoader_app1",
54+
"UnloadUnregisteredLoader",
55+
"UnloadUnregisteredLoader$CustomLoader",
56+
"CustomLoadee5",
57+
"Util");
5358
String appJar2 = JarBuilder.build(true, "UnloadUnregisteredLoader_app2",
5459
"jdk/test/lib/classloader/ClassUnloadCommon",
5560
"jdk/test/lib/classloader/ClassUnloadCommon$1",
5661
"jdk/test/lib/classloader/ClassUnloadCommon$TestFailure");
57-
String customJarPath = JarBuilder.build("UnloadUnregisteredLoader_custom", "CustomLoadee");
62+
String customJarPath = JarBuilder.build("UnloadUnregisteredLoader_custom",
63+
"CustomLoadee",
64+
"CustomLoadee5",
65+
"CustomLoadee5Child");
5866
String wbJar = JarBuilder.build(true, "WhiteBox", "jdk/test/whitebox/WhiteBox");
5967
String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
6068

@@ -66,6 +74,8 @@ public static void main(String[] args) throws Exception {
6674
"jdk/test/lib/classloader/ClassUnloadCommon$TestFailure",
6775
"java/lang/Object id: 1",
6876
"CustomLoadee id: 2 super: 1 source: " + customJarPath,
77+
"CustomLoadee5 id: 3 super: 1 source: " + customJarPath,
78+
"CustomLoadee5Child id: 4 super: 3 source: " + customJarPath,
6979
};
7080

7181
OutputAnalyzer output;

test/hotspot/jtreg/runtime/cds/appcds/customLoader/test-classes/CustomLoadee5.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* questions.
2222
*/
2323

24-
class CustomLoadee5 {
24+
public class CustomLoadee5 {
2525
public String toString() {
2626
return "this is CustomLoadee5";
2727
}

test/hotspot/jtreg/runtime/cds/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424

2525
import java.io.File;
26+
import java.io.InputStream;
2627
import java.net.URL;
2728
import java.net.URLClassLoader;
2829
import jdk.test.whitebox.WhiteBox;
@@ -46,9 +47,11 @@ public static void main(String args[]) throws Exception {
4647
}
4748
}
4849

49-
public static void doit(URL urls[], String className, boolean isFirstTime) throws Exception {
50+
public static void doit(URL urls[], String className, boolean isFirstTime) throws Exception {
5051
ClassLoader appLoader = UnloadUnregisteredLoader.class.getClassLoader();
51-
URLClassLoader custLoader = new URLClassLoader(urls, appLoader);
52+
CustomLoader custLoader = new CustomLoader(urls, appLoader);
53+
54+
// Part 1 -- load CustomLoadee. It should be loaded from archive when isFirstTime==true
5255

5356
Class klass = custLoader.loadClass(className);
5457
WhiteBox wb = WhiteBox.getWhiteBox();
@@ -68,5 +71,43 @@ public static void doit(URL urls[], String className, boolean isFirstTime) throw
6871
}
6972
}
7073
}
74+
75+
// Part 2
76+
//
77+
// CustomLoadee5 is never loaded from the archive, because the classfile bytes don't match
78+
// CustomLoadee5Child is never loaded from the archive, its super is not loaded from the archive
79+
try (InputStream in = appLoader.getResourceAsStream("CustomLoadee5.class")) {
80+
byte[] b = in.readAllBytes();
81+
Util.replace(b, "this is", "DAS IST"); // Modify the bytecodes
82+
Class<?> c = custLoader.myDefineClass(b, 0, b.length);
83+
System.out.println(c.newInstance());
84+
if (!"DAS IST CustomLoadee5".equals(c.newInstance().toString())) {
85+
throw new RuntimeException("Bytecode modification not successful");
86+
}
87+
if (wb.isSharedClass(c)) {
88+
throw new RuntimeException(c + "should not be loaded from CDS");
89+
}
90+
}
91+
92+
// When isFirstTime==true, the VM will try to load the archived copy of CustomLoadee5Child,
93+
// but it will fail (because CustomLoadee5 was not loaded from the archive) and will recover
94+
// by decoding the class from its classfile data.
95+
// This failure should not leave the JVM in an inconsistent state.
96+
Class<?> child = custLoader.loadClass("CustomLoadee5Child");
97+
if (wb.isSharedClass(child)) {
98+
throw new RuntimeException(child + "should not be loaded from CDS");
99+
}
100+
}
101+
102+
static class CustomLoader extends URLClassLoader {
103+
public CustomLoader(URL[] urls, ClassLoader parent) {
104+
super(urls, parent);
105+
}
106+
107+
public Class<?> myDefineClass(byte[] b, int off, int len)
108+
throws ClassFormatError
109+
{
110+
return super.defineClass(b, off, len);
111+
}
71112
}
72113
}

0 commit comments

Comments
 (0)