Skip to content

Commit

Permalink
8238460: Provide warnings about the use of JNI RegisterNatives to reb…
Browse files Browse the repository at this point in the history
…ind native methods for boot/platform classes in other classloaders

Reviewed-by: jwilhelm, lfoltan
  • Loading branch information
David Holmes committed Feb 7, 2020
1 parent ccbd819 commit 9f21d80
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
22 changes: 21 additions & 1 deletion src/hotspot/share/prims/jni.cpp
Expand Up @@ -2811,6 +2811,20 @@ JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,

Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz));

// There are no restrictions on native code registering native methods, which
// allows agents to redefine the bindings to native methods. But we issue a
// warning if any code running outside of the boot/platform loader is rebinding
// any native methods in classes loaded by the boot/platform loader.
Klass* caller = thread->security_get_caller_class(1);
bool do_warning = false;
oop cl = k->class_loader();
if (cl == NULL || SystemDictionary::is_platform_class_loader(cl)) {
// If no caller class, or caller class has a different loader, then
// issue a warning below.
do_warning = (caller == NULL) || caller->class_loader() != cl;
}


for (int index = 0; index < nMethods; index++) {
const char* meth_name = methods[index].name;
const char* meth_sig = methods[index].signature;
Expand All @@ -2823,13 +2837,19 @@ JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,
TempNewSymbol signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig));

if (name == NULL || signature == NULL) {
ResourceMark rm;
ResourceMark rm(THREAD);
stringStream st;
st.print("Method %s.%s%s not found", k->external_name(), meth_name, meth_sig);
// Must return negative value on failure
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
}

if (do_warning) {
ResourceMark rm(THREAD);
log_warning(jni, resolve)("Re-registering of platform native method: %s.%s%s "
"from code in a different classloader", k->external_name(), meth_name, meth_sig);
}

bool res = Method::register_native(k, name, signature,
(address) methods[index].fnPtr, THREAD);
if (!res) {
Expand Down
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2020, 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 jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

/*
* @test
* @bug 8238460
* @summary Check that re-registering a native method of a boot class
* generates a warning when not done from a boot class
*
* @library /test/lib
* @run main/othervm/native TestRegisterNativesWarning
*/

public class TestRegisterNativesWarning {

static {
System.loadLibrary("registerNativesWarning");
}

/*
* We will replace:
* java/lang/Thread.java: public static native void yield();
*
* as it is simple and innocuous.
*/
native static void test(Class<?> jlThread);

// Using a nested class that invokes an enclosing method makes it
// easier to setup and use the native library.
static class Tester {
public static void main(String[] args) throws Exception {
System.out.println("Running test() in class loader " +
Tester.class.getClassLoader());
test(Thread.class);
Thread.yield();
}
}

public static void main(String[] args) throws Exception {
String warning = "Re-registering of platform native method: java.lang.Thread.yield()V from code in a different classloader";

OutputAnalyzer output = ProcessTools.executeTestJvm(Tester.class.getName());
output.shouldContain(warning);
output.shouldHaveExitValue(0);
output.reportDiagnosticSummary();

// If we run everything from the "boot" loader there should be no warning
String cp = System.getProperty("test.class.path");
String libp = System.getProperty("java.library.path");
output = ProcessTools.executeTestJvm("-Xbootclasspath/a:" + cp,
"-Dsun.boot.library.path=" + libp,
Tester.class.getName());
output.shouldNotContain(warning);
output.shouldHaveExitValue(0);
output.reportDiagnosticSummary();
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, 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 "jni.h"
#include "jni_util.h"

/*
* We will replace:
* java/lang/Thread.java: public static native void yield();
*
* as it is simple and innocuous.
*/
static void myYield(JNIEnv* env, jclass cls) {
printf("myYield executed\n");
}


JNIEXPORT void JNICALL
Java_TestRegisterNativesWarning_test
(JNIEnv *env, jclass cls, jclass jlThread) {
JNINativeMethod nativeMethods[] = {
{
(char*) "yield", // name
(char*) "()V", // sig
(void*) myYield // native method ptr
}
};
(*env)->RegisterNatives(env, jlThread, nativeMethods, 1);
}

0 comments on commit 9f21d80

Please sign in to comment.