8267347: CDS record_linking_constraint asserts with unregistered class

Reviewed-by: minqi, ccheung
This commit is contained in:
Ioi Lam 2021-05-24 21:52:13 +00:00
parent a5467ae7bb
commit 209769b5ad
5 changed files with 156 additions and 22 deletions
src/hotspot/share/classfile
test/hotspot/jtreg/runtime/cds/appcds

@ -1855,8 +1855,6 @@ void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKla
// either of these two loaders. The check itself does not
// try to resolve T.
oop klass_loader = klass->class_loader();
assert(klass_loader != NULL, "should not be called for boot loader");
assert(loader1 != loader2, "must be");
if (!is_system_class_loader(klass_loader) &&
!is_platform_class_loader(klass_loader)) {
@ -1872,6 +1870,17 @@ void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKla
return;
}
if (DumpSharedSpaces && !is_builtin(klass)) {
// During static dump, unregistered classes (those intended for
// custom loaders) are loaded by the boot loader. Need to
// exclude these for the same reason as above.
// This should be fixed by JDK-8261941.
return;
}
assert(klass_loader != NULL, "should not be called for boot loader");
assert(loader1 != loader2, "must be");
if (DynamicDumpSharedSpaces && Thread::current()->is_VM_thread()) {
// We are re-laying out the vtable/itables of the *copy* of
// a class during the final stage of dynamic dumping. The

@ -25,6 +25,7 @@
* @test
* @requires vm.cds
* @summary Test class loader constraint checks for archived classes (dynamic archive)
* @bug 8267347
* @library /test/lib
* /test/hotspot/jtreg/runtime/cds/appcds
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
@ -40,6 +41,7 @@ import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import jdk.test.lib.Asserts;
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.Platform;
public class DynamicLoaderConstraintsTest extends DynamicArchiveTestBase {
static String mainClass = LoaderConstraintsApp.class.getName();
@ -55,14 +57,27 @@ public class DynamicLoaderConstraintsTest extends DynamicArchiveTestBase {
MyClassLoader.class.getName()
};
static String loaderMainClass = CustomAppLoader.class.getName();
static String loaderJar = null;
static String loaderClasses[] = {
loaderMainClass
};
public static void main(String[] args) throws Exception {
runTest(DynamicLoaderConstraintsTest::doTest);
}
static void doTest() throws Exception {
appJar = ClassFileInstaller.writeJar("loader_constraints.jar", appClasses);
doTest(false);
doTest(true);
doTest(false, false);
doTest(true, false);
if (!Platform.isWindows()) {
// custom loaders are not supported on Windows yet.
loaderJar = ClassFileInstaller.writeJar("custom_app_loader.jar", loaderClasses);
doTest(false, true);
doTest(true, true);
}
}
/*
@ -74,21 +89,34 @@ public class DynamicLoaderConstraintsTest extends DynamicArchiveTestBase {
* causing LinkageError. This ensures the test classes will be
* archived so we can test CDS's handling of loader constraints during
* run time.
*
* useCustomLoader: if true, load the LoaderConstraintsApp in a custom loader before executing it.
* if false, LoaderConstraintsApp will be loaded by the built-in AppClassLoader.
*/
static void doTest(boolean errorInDump) throws Exception {
static void doTest(boolean errorInDump, boolean useCustomLoader) throws Exception {
for (int i = 1; i <= 3; i++) {
System.out.println("========================================");
System.out.println("errorInDump: " + errorInDump + ", useCustomLoader: " + useCustomLoader + ", case: " + i);
System.out.println("========================================");
String topArchiveName = getNewArchiveName();
String testCase = Integer.toString(i);
String cmdLine[] = new String[] {
"-cp", appJar,
"--add-modules",
"java.base,jdk.httpserver",
"--add-exports",
"java.base/jdk.internal.misc=ALL-UNNAMED",
"-Xlog:class+load,class+loader+constraints",
mainClass, testCase
};
if (useCustomLoader) {
cmdLine = TestCommon.concat(cmdLine, "-cp", loaderJar,
loaderMainClass, appJar);
} else {
cmdLine = TestCommon.concat(cmdLine, "-cp", appJar);
}
cmdLine = TestCommon.concat(cmdLine, mainClass, testCase);
String[] dumpCmdLine = cmdLine;
if (!errorInDump) {
dumpCmdLine = TestCommon.concat(dumpCmdLine, "loadClassOnly");

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -36,7 +36,7 @@ import java.net.URL;
// then try to define HttpExchange in child (app) loader => fail.
// 3. no LinkageError should be throw when linking a class that does not override/implement any
// methods.
class LoaderConstraintsApp {
public class LoaderConstraintsApp {
static void defineHttpExchangeWithAppLoader() throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();
URL url = new URL("jrt://modules/jdk.httpserver/com/sun/net/httpserver/HttpExchange.class");

@ -25,6 +25,7 @@
* @test
* @requires vm.cds
* @summary Test class loader constraint checks for archived classes
* @bug 8267347
* @library /test/lib
* /test/hotspot/jtreg/runtime/cds/appcds
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
@ -37,14 +38,17 @@ import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import jdk.test.lib.Asserts;
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.Platform;
public class LoaderConstraintsTest {
static String mainClass = LoaderConstraintsApp.class.getName();
static String httpHandlerClass = HttpHandler.class.getName().replace(".", "/");
static String httpExchangeClass = HttpExchange.class.getName().replace(".", "/");
static String appJar = null;
static String appClasses[] = {
mainClass,
HttpHandler.class.getName(),
HttpExchange.class.getName(),
httpHandlerClass,
httpExchangeClass,
Asserts.class.getName(),
MyHttpHandler.class.getName(),
MyHttpHandlerB.class.getName(),
@ -52,18 +56,50 @@ public class LoaderConstraintsTest {
MyClassLoader.class.getName()
};
static String loaderMainClass = CustomAppLoader.class.getName();
static String loaderJar = null;
static String loaderClasses[] = {
loaderMainClass
};
static void doTest() throws Exception {
appJar = ClassFileInstaller.writeJar("loader_constraints.jar", appClasses);
TestCommon.dump(appJar, appClasses, "-Xlog:cds+load");
String joptsMain[] = TestCommon.concat("-cp", appJar,
"-Xlog:cds",
"-Xlog:class+loader+constraints=debug",
"--add-exports",
"java.base/jdk.internal.misc=ALL-UNNAMED",
mainClass);
runWithArchive(joptsMain, "1");
runWithArchive(joptsMain, "2");
runWithArchive(joptsMain, "3");
TestCommon.dump(appJar, appClasses, "-Xlog:cds");
String cmdLine[] =
TestCommon.concat("-cp", appJar,
"-Xlog:cds",
"-Xlog:class+loader+constraints=debug",
"--add-exports",
"java.base/jdk.internal.misc=ALL-UNNAMED",
mainClass);
runWithArchive(cmdLine, "1");
runWithArchive(cmdLine, "2");
runWithArchive(cmdLine, "3");
}
// Same as doTest, except that LoaderConstraintsApp and MyHttpHandler* are loaded
// by a custom loader. This is test case for JDK-8267347.
static void doTestCustomLoader() throws Exception {
String src = " source: " + appJar;
String classList[] =
TestCommon.concat(loaderClasses,
"java/lang/Object id: 1",
mainClass + " id: 2 super: 1" + src,
httpHandlerClass + " id: 3",
"MyHttpHandler id: 5 super: 1 interfaces: 3" + src,
"MyHttpHandlerB id: 6 super: 1 interfaces: 3" + src,
"MyHttpHandlerC id: 7 super: 1 interfaces: 3" + src);
TestCommon.dump(loaderJar, classList, "-Xlog:cds");
String cmdLine[] =
TestCommon.concat("-cp", loaderJar,
"-Xlog:cds",
"-Xlog:class+loader+constraints=debug",
"--add-exports",
"java.base/jdk.internal.misc=ALL-UNNAMED",
loaderMainClass, appJar, mainClass);
runWithArchive(cmdLine, "1");
runWithArchive(cmdLine, "2");
runWithArchive(cmdLine, "3");
}
static void runWithArchive(String[] optsMain, String arg) throws Exception {
@ -72,7 +108,13 @@ public class LoaderConstraintsTest {
}
public static void main(String... args) throws Exception {
appJar = ClassFileInstaller.writeJar("loader_constraints.jar", appClasses);
doTest();
if (!Platform.isWindows()) {
// custom loaders are not supported on Windows yet.
loaderJar = ClassFileInstaller.writeJar("custom_app_loader.jar", loaderClasses);
doTestCustomLoader();
}
}
}

@ -0,0 +1,55 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
// This is a handy class for running an application inside a custom class loader. This
// is used for testing CDS handling of unregistered classes (i.e., archived classes loaded
// by custom class loaders).
//
// See test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java
// for an example.
public class CustomAppLoader {
// args[0] = App JAR file
// args[1] = App main class
// args[2...] = arguments for the main class
public static void main(String args[]) throws Throwable {
File f = new File(args[0]);
URL[] classLoaderUrls = new URL[] {new URL("file://" + f.getCanonicalPath())};
URLClassLoader loader = new URLClassLoader(classLoaderUrls, CustomAppLoader.class.getClassLoader());
Class k = Class.forName(args[1], true, loader);
Class parameterTypes[] = new Class[] {String[].class};
Method mainMethod = k.getDeclaredMethod("main", parameterTypes);
String appArgs[] = new String[args.length - 2];
Object invokeArgs[] = new Object[] {appArgs};
for (int i = 0; i < appArgs.length; i++) {
appArgs[i] = args[i + 2];
}
mainMethod.invoke(null, invokeArgs);
}
}