8221351: Crash in KlassFactory::check_shared_class_file_load_hook

Reviewed-by: dholmes, ccheung
This commit is contained in:
Ioi Lam 2019-03-29 08:42:32 -07:00
parent b6cdfa908f
commit e2ffa15762
13 changed files with 195 additions and 33 deletions

@ -396,6 +396,10 @@ ClassPathImageEntry::~ClassPathImageEntry() {
}
}
ClassFileStream* ClassPathImageEntry::open_stream(const char* name, TRAPS) {
return open_stream_for_loader(name, ClassLoaderData::the_null_class_loader_data(), THREAD);
}
// For a class in a named module, look it up in the jimage file using this syntax:
// /<module-name>/<package-name>/<base-class>
//
@ -403,7 +407,7 @@ ClassPathImageEntry::~ClassPathImageEntry() {
// 1. There are no unnamed modules in the jimage file.
// 2. A package is in at most one module in the jimage file.
//
ClassFileStream* ClassPathImageEntry::open_stream(const char* name, TRAPS) {
ClassFileStream* ClassPathImageEntry::open_stream_for_loader(const char* name, ClassLoaderData* loader_data, TRAPS) {
jlong size;
JImageLocationRef location = (*JImageFindResource)(_jimage, "", get_jimage_version_string(), name, &size);
@ -414,20 +418,8 @@ ClassFileStream* ClassPathImageEntry::open_stream(const char* name, TRAPS) {
if (pkg_name != NULL) {
if (!Universe::is_module_initialized()) {
location = (*JImageFindResource)(_jimage, JAVA_BASE_NAME, get_jimage_version_string(), name, &size);
#if INCLUDE_CDS
// CDS uses the boot class loader to load classes whose packages are in
// modules defined for other class loaders. So, for now, get their module
// names from the "modules" jimage file.
if (DumpSharedSpaces && location == 0) {
const char* module_name = (*JImagePackageToModule)(_jimage, pkg_name);
if (module_name != NULL) {
location = (*JImageFindResource)(_jimage, module_name, get_jimage_version_string(), name, &size);
}
}
#endif
} else {
PackageEntry* package_entry = ClassLoader::get_package_entry(name, ClassLoaderData::the_null_class_loader_data(), CHECK_NULL);
PackageEntry* package_entry = ClassLoader::get_package_entry(name, loader_data, CHECK_NULL);
if (package_entry != NULL) {
ResourceMark rm;
// Get the module name

@ -61,6 +61,10 @@ public:
// Attempt to locate file_name through this class path entry.
// Returns a class file parsing stream if successfull.
virtual ClassFileStream* open_stream(const char* name, TRAPS) = 0;
// Open the stream for a specific class loader
virtual ClassFileStream* open_stream_for_loader(const char* name, ClassLoaderData* loader_data, TRAPS) {
return open_stream(name, THREAD);
}
};
class ClassPathDirEntry: public ClassPathEntry {
@ -125,6 +129,7 @@ public:
ClassPathImageEntry(JImageFile* jimage, const char* name);
virtual ~ClassPathImageEntry();
ClassFileStream* open_stream(const char* name, TRAPS);
ClassFileStream* open_stream_for_loader(const char* name, ClassLoaderData* loader_data, TRAPS);
};
// ModuleClassPathList contains a linked list of ClassPathEntry's

@ -58,7 +58,7 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook(
// Post the CFLH
JvmtiCachedClassFileData* cached_class_file = NULL;
if (cfs == NULL) {
cfs = FileMapInfo::open_stream_for_jvmti(ik, CHECK_NULL);
cfs = FileMapInfo::open_stream_for_jvmti(ik, class_loader, CHECK_NULL);
}
unsigned char* ptr = (unsigned char*)cfs->buffer();
unsigned char* end_ptr = ptr + cfs->length();

@ -24,7 +24,9 @@
#include "precompiled.hpp"
#include "jvm.h"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.inline.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/classLoaderExt.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
@ -1489,7 +1491,7 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) {
return ent;
}
ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, TRAPS) {
ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, Handle class_loader, TRAPS) {
int path_index = ik->shared_classpath_index();
assert(path_index >= 0, "should be called for shared built-in classes only");
assert(path_index < (int)_shared_path_table_size, "sanity");
@ -1501,7 +1503,12 @@ ClassFileStream* FileMapInfo::open_stream_for_jvmti(InstanceKlass* ik, TRAPS) {
const char* const class_name = name->as_C_string();
const char* const file_name = ClassLoader::file_name_for_class_name(class_name,
name->utf8_length());
return cpe->open_stream(file_name, THREAD);
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
ClassFileStream* cfs = cpe->open_stream_for_loader(file_name, loader_data, THREAD);
assert(cfs != NULL, "must be able to read the classfile data of shared classes for built-in loaders.");
log_debug(cds, jvmti)("classfile data for %s [%d: %s] = %d bytes", class_name, path_index,
cfs->source(), cfs->length());
return cfs;
}
#endif

@ -303,7 +303,7 @@ public:
static void update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS);
#if INCLUDE_JVMTI
static ClassFileStream* open_stream_for_jvmti(InstanceKlass* ik, TRAPS);
static ClassFileStream* open_stream_for_jvmti(InstanceKlass* ik, Handle class_loader, TRAPS);
#endif
static SharedClassPathEntry* shared_path(int index) {

@ -321,6 +321,21 @@ tier1_runtime_appcds_exclude = \
runtime/appcds/ \
-:tier1_runtime_appcds
# This group should be executed with "jtreg -Dtest.cds.run.with.jfr=true ..."
# to test interaction between AppCDS and JFR. It also has the side effect of
# testing JVMTI ClassFileLoadHook.
#
# The excluded tests disallow the jdk.jfr module, which is required to
# run with JFR.
hotspot_appcds_with_jfr = \
runtime/appcds/ \
-runtime/appcds/cacheObject/ArchivedModuleCompareTest.java \
-runtime/appcds/jigsaw/classpathtests/BootAppendTests.java \
-runtime/appcds/jigsaw/classpathtests/ClassPathTests.java \
-runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java \
-runtime/appcds/jigsaw/JigsawOptionsCombo.java \
-runtime/appcds/jigsaw/modulepath/MainModuleOnly.java
tier1_serviceability = \
serviceability/dcmd/compiler \
-serviceability/dcmd/compiler/CompilerQueueTest.java \

@ -145,6 +145,15 @@ public class TestCommon extends CDSTestUtils {
return executeAndLog(pb, "dump");
}
// This allows you to run the AppCDS tests with JFR enabled at runtime (though not at
// dump time, as that's uncommon for typical AppCDS users).
//
// To run in this special mode, add the following to your jtreg command-line
// -Dtest.cds.run.with.jfr=true
//
// Some AppCDS tests are not compatible with this mode. See the group
// hotspot_appcds_with_jfr in ../../TEST.ROOT for details.
private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr");
// Execute JVM using AppCDS archive with specified AppCDSOptions
public static OutputAnalyzer runWithArchive(AppCDSOptions opts)
@ -166,6 +175,22 @@ public class TestCommon extends CDSTestUtils {
for (String s : opts.suffix) cmd.add(s);
if (RUN_WITH_JFR) {
boolean usesJFR = false;
for (String s : cmd) {
if (s.startsWith("-XX:StartFlightRecording=") || s.startsWith("-XX:FlightRecorderOptions")) {
System.out.println("JFR option might have been specified. Don't interfere: " + s);
usesJFR = true;
break;
}
}
if (!usesJFR) {
System.out.println("JFR option not specified. Enabling JFR ...");
cmd.add(0, "-XX:StartFlightRecording=dumponexit=true");
System.out.println(cmd);
}
}
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
return executeAndLog(pb, "exec");

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2019, 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
@ -28,8 +28,6 @@
* @requires vm.cds
* @requires vm.cds.custom.loaders
* @library /test/lib /test/hotspot/jtreg/runtime/appcds
* @modules java.base/jdk.internal.misc
* java.management
* @compile test-classes/Hello.java test-classes/CustomLoadee.java
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar hello.jar Hello
@ -43,6 +41,9 @@ import sun.hotspot.WhiteBox;
public class HelloCustom {
public static void main(String[] args) throws Exception {
run();
}
public static void run(String... extra_runtime_args) throws Exception {
String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
@ -62,11 +63,12 @@ public class HelloCustom {
use_whitebox_jar);
output = TestCommon.exec(appJar,
// command-line arguments ...
use_whitebox_jar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"Hello", customJarPath);
TestCommon.concat(extra_runtime_args,
// command-line arguments ...
use_whitebox_jar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"Hello", customJarPath));
TestCommon.checkExec(output);
}
}

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019, 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.
*
*/
/*
* @test
* @summary Same as HelloCustom, but add -XX:StartFlightRecording=dumponexit=true to the runtime
* options. This makes sure that the shared classes are compatible with both
* JFR and JVMTI ClassFileLoadHook.
* @requires vm.hasJFR
* @requires vm.cds
* @requires vm.cds.custom.loaders
* @library /test/lib /test/hotspot/jtreg/runtime/appcds
* @compile test-classes/Hello.java test-classes/CustomLoadee.java
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar hello.jar Hello
* @run driver ClassFileInstaller -jar hello_custom.jar CustomLoadee
* @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
* @run driver HelloCustom_JFR
*/
import jdk.test.lib.process.OutputAnalyzer;
import sun.hotspot.WhiteBox;
public class HelloCustom_JFR {
public static void main(String[] args) throws Exception {
HelloCustom.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug");
}
}

@ -93,6 +93,10 @@ public class ModulePathAndCP {
}
public static void main(String... args) throws Exception {
run();
}
public static void run(String... extra_runtime_args) throws Exception {
// compile the modules and create the modular jar files
buildTestModule();
String appClasses[] = {MAIN_CLASS, APP_CLASS};
@ -104,6 +108,7 @@ public class ModulePathAndCP {
"-m", MAIN_MODULE);
TestCommon.checkDump(output);
String prefix[] = {"-Djava.class.path=", "-Xlog:class+load=trace"};
prefix = TestCommon.concat(prefix, extra_runtime_args);
// run with the archive with the --module-path the same as the one during
// dump time. The classes should be loaded from the archive.

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019, 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.
*
*/
/**
* @test
* @requires vm.hasJFR & vm.cds
* @library /test/lib /test/hotspot/jtreg/runtime/appcds
* @modules jdk.compiler
* jdk.jartool/sun.tools.jar
* jdk.jlink
* @run driver ModulePathAndCP_JFR
* @summary Same as ModulePathAndCP, but add -XX:StartFlightRecording=dumponexit=true to the runtime
* options. This makes sure that the shared classes are compatible with both
* JFR and JVMTI ClassFileLoadHook.
*/
public class ModulePathAndCP_JFR {
public static void main(String... args) throws Exception {
ModulePathAndCP.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug");
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2019, 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
@ -41,8 +41,14 @@ public class ClassFileLoadHook {
SHARING_ON_CFLH_ON
}
public static void main(String args[]) {
public static void main(String args[]) throws Exception {
TestCaseId testCase = TestCaseId.valueOf(args[0]);
test1(testCase);
test2(testCase);
}
// Test rewriting the classfile data using CFLH
static void test1(TestCaseId testCase) {
WhiteBox wb = WhiteBox.getWhiteBox();
System.out.println("====== ClassFileLoadHook.main():testCase = " + testCase);
@ -81,6 +87,20 @@ public class ClassFileLoadHook {
}
}
// Test the loading of classfile data for non-boot shared classes from jrt:/xxx.
// See JDK-8221351.
static void test2(TestCaseId testCase) throws Exception {
WhiteBox wb = WhiteBox.getWhiteBox();
Class c = Class.forName("java.sql.SQLException"); // defined by platform class loader.
switch (testCase) {
case SHARING_ON_CFLH_OFF:
case SHARING_AUTO_CFLH_ON:
case SHARING_ON_CFLH_ON:
assertTrue(wb.isSharedClass(c), "must be shared");
}
}
private static void assertTrue(boolean expr, String msg) {
if (!expr)
throw new RuntimeException(msg);

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2019, 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
@ -27,9 +27,6 @@
* @summary Test jvmti class file loader hook interaction with AppCDS
* @library /test/lib /test/hotspot/jtreg/runtime/appcds
* @requires vm.cds
* @modules java.base/jdk.internal.misc
* jdk.jartool/sun.tools.jar
* java.management
* @build ClassFileLoadHook
* @run main/othervm/native ClassFileLoadHookTest
*/
@ -46,7 +43,8 @@ public class ClassFileLoadHookTest {
"ClassFileLoadHook",
"ClassFileLoadHook$TestCaseId",
"ClassFileLoadHook$1",
"LoadMe"
"LoadMe",
"java/sql/SQLException"
};
public static void main(String[] args) throws Exception {