Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JDK-8281000 ClassLoader::registerAsParallelCapable throws NPE if caller is null #7448

Closed
wants to merge 10 commits into from
2 changes: 2 additions & 0 deletions make/test/JtregNativeJdk.gmk
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ ifeq ($(call isTargetOs, windows), true)
WIN_LIB_JLI := $(SUPPORT_OUTPUTDIR)/native/java.base/libjli/jli.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeJliLaunchTest := $(WIN_LIB_JLI)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerClassLoaderTest := jvm.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerLookupTest := jvm.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc
@@ -81,6 +82,7 @@ else
endif
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeJliLaunchTest := -ljli
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := -ljvm
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerClassLoaderTest := -ljvm
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerLookupTest := -ljvm
endif

10 changes: 10 additions & 0 deletions src/java.base/share/classes/java/lang/ClassLoader.java
Original file line number Diff line number Diff line change
@@ -1606,9 +1606,16 @@ protected Enumeration<URL> findResources(String name) throws IOException {
* </ol>
* <p>Note that once a class loader is registered as parallel capable, there
* is no way to change it back.</p>
* <p>
* In cases where this method is called from a context where the caller is
* not a subclass of {@code ClassLoader} or there is no caller frame on the
* stack (e.g. when called directly from a JNI attached thread),
* {@code IllegalCallerException} is thrown.
* </p>
*
* @return {@code true} if the caller is successfully registered as
* parallel capable and {@code false} if otherwise.
* @throws IllegalCallerException if the caller is not a subclass of {@code ClassLoader}
*
* @see #isRegisteredAsParallelCapable()
*
@@ -1622,6 +1629,9 @@ protected static boolean registerAsParallelCapable() {
// Caller-sensitive adapter method for reflective invocation
@CallerSensitiveAdapter
private static boolean registerAsParallelCapable(Class<?> caller) {
if ((caller == null) || !ClassLoader.class.isAssignableFrom(caller)) {
throw new IllegalCallerException(caller + " not a subclass of ClassLoader");
}
return ParallelLoaders.register(caller.asSubclass(ClassLoader.class));
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2022, 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
* @bug 8281000
* @summary Test ClassLoader.isRegisteredAsParallelCapable() method with an
* invalid caller (non-JNI cases). This test uses reflection and opens
* the java.base module so it runs separate from behavior tests of
* isRegisteredParallelCapable to avoid side effects.
* @modules java.base/java.lang:open
* @run main/othervm BadRegisterAsParallelCapableCaller
*/

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BadRegisterAsParallelCapableCaller {

public static void main(String[] args) throws Exception {

Throwable thrown = null;
final Method m = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
m.setAccessible(true);
try {
m.invoke(null);
} catch (InvocationTargetException ite) {
thrown = ite.getCause();
}
if (! (thrown instanceof IllegalCallerException)) {
throw new RuntimeException("Didn't get expected IllegalCallerException, got "+ thrown);
} else {
System.out.println("Invalid caller threw IllegalCallerException as expected");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2022, 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
* @bug 8281000
* @summary Test uses custom launcher that starts VM using JNI that verifies
* ClassLoader.registerAsParallelCapable with null caller class
* throws an IllegalCallerException.
* @library /test/lib
* @requires os.family != "aix"
* @run main/native NullCallerClassLoaderTest
*/

// Test disabled on AIX since we cannot invoke the JVM on the primordial thread.

import java.io.File;
import java.util.Map;
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class NullCallerClassLoaderTest {
public static void main(String[] args) throws IOException {
Path launcher = Path.of(System.getProperty("test.nativepath"), "NullCallerClassLoaderTest");
ProcessBuilder pb = new ProcessBuilder(launcher.toString());
Map<String, String> env = pb.environment();

String libDir = Platform.libDir().toString();
String vmDir = Platform.jvmLibDir().toString();

// set up shared library path
String sharedLibraryPathEnvName = Platform.sharedLibraryPathVariableName();
env.compute(sharedLibraryPathEnvName,
(k, v) -> (v == null) ? libDir : v + File.pathSeparator + libDir);
env.compute(sharedLibraryPathEnvName,
(k, v) -> (v == null) ? vmDir : v + File.pathSeparator + vmDir);

System.out.println("Launching: " + launcher + " shared library path: " +
env.get(sharedLibraryPathEnvName));
new OutputAnalyzer(pb.start())
.outputTo(System.out)
.errorTo(System.err)
.shouldHaveExitValue(0);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2022, 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.
*/

#include <stdio.h>
#include <stdlib.h>

#include "jni.h"
#include "assert.h"

static jclass class_IllegalCallerException;

int checkAndClearIllegalCallerExceptionThrown(JNIEnv *env) {
jthrowable t = (*env)->ExceptionOccurred(env);
if ((*env)->IsInstanceOf(env, t, class_IllegalCallerException) == JNI_TRUE) {
(*env)->ExceptionClear(env);
return JNI_TRUE;
}
return JNI_FALSE;
}

int main(int argc, char** args) {
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
jint rc;


vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.options = options;

if ((rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)) != JNI_OK) {
printf("ERROR: cannot create VM.\n");
exit(-1);
}
class_IllegalCallerException = (*env)->FindClass(env, "java/lang/IllegalCallerException");
assert (class_IllegalCallerException != NULL);

// call ClassLoader.registerAsParallelCapable()
jclass class_ClassLoader = (*env)->FindClass(env, "java/lang/ClassLoader");
assert(class_ClassLoader != NULL);
jmethodID mid_ClassLoader_registerAsParallelCapable = (*env)->GetStaticMethodID(env, class_ClassLoader, "registerAsParallelCapable", "()Z" );
assert(mid_ClassLoader_registerAsParallelCapable != NULL);
jboolean b = (*env)->CallStaticBooleanMethod(env, class_ClassLoader, mid_ClassLoader_registerAsParallelCapable );
if ((rc = checkAndClearIllegalCallerExceptionThrown(env)) != JNI_TRUE) {
printf("ERROR: Didn't get the expected IllegalCallerException.\n");
exit(-1);
}

printf("Expected IllegalCallerException was thrown\n");

(*jvm)->DestroyJavaVM(jvm);
return 0;
}