diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index c9841ca198c..40d99a48103 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -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 @@ -72,6 +72,8 @@ else BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeJliLaunchTest := -ljli endif +BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := -ljvm + ifeq ($(call isTargetOs, macosx), true) BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libTestMainKeyWindow := -ObjC BUILD_JDK_JTREG_LIBRARIES_LIBS_libTestMainKeyWindow := -framework JavaVM \ diff --git a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java index 9baffbe7733..f4f52586911 100644 --- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java +++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -56,7 +56,10 @@ import sun.security.util.SecurityConstants; * {@code Field}s, {@code Method}s, or {@code Constructor}s are used to get or * set fields, to invoke methods, or to create and initialize new instances of * classes, respectively. Every reflected object checks that the code using it - * is in an appropriate class, package, or module.
+ * is in an appropriate class, package, or module. The check when invoked by + * JNI code with no Java + * class on the stack only succeeds if the member and the declaring class are + * public, and the class is in a package that is exported to all modules. * * The one variation from Java language access control is that the checks
* by reflected objects assume readability. That is, the module containing
@@ -670,6 +673,13 @@ public class AccessibleObject implements AnnotatedElement {
private boolean slowVerifyAccess(Class> caller, Class> memberClass,
Class> targetClass, int modifiers)
{
+
+ if (caller == null) {
+ // No caller frame when a native thread attaches to the VM
+ // only allow access to a public accessible member
+ return Reflection.verifyPublicMemberAccess(memberClass, modifiers);
+ }
+
if (!Reflection.verifyMemberAccess(caller, memberClass, targetClass, modifiers)) {
// access denied
return false;
diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
index 492103209aa..4b4e1c3cea0 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -122,6 +122,9 @@ public class Reflection {
Class> targetClass,
int modifiers)
{
+ Objects.requireNonNull(currentClass);
+ Objects.requireNonNull(memberClass);
+
if (currentClass == memberClass) {
// Always succeeds
return true;
@@ -201,6 +204,22 @@ public class Reflection {
return true;
}
+ /*
+ * Verify if a member is public and memberClass is a public type
+ * in a package that is unconditionally exported and
+ * return {@code true}if it is granted.
+ *
+ * @param memberClass the declaring class of the member being accessed
+ * @param modifiers the member's access modifiers
+ * @return {@code true} if the member is public and in a publicly accessible type
+ */
+ public static boolean verifyPublicMemberAccess(Class> memberClass, int modifiers) {
+ Module m = memberClass.getModule();
+ return Modifier.isPublic(modifiers)
+ && m.isExported(memberClass.getPackageName())
+ && Modifier.isPublic(Reflection.getClassAccessFlags(memberClass));
+ }
+
/**
* Returns {@code true} if memberClass's module exports memberClass's
* package to currentModule.
@@ -325,8 +344,10 @@ public class Reflection {
Class> memberClass,
Class> targetClass,
int modifiers)
- throws IllegalAccessException
{
+ if (currentClass == null)
+ return newIllegalAccessException(memberClass, modifiers);
+
String currentSuffix = "";
String memberSuffix = "";
Module m1 = currentClass.getModule();
@@ -355,6 +376,36 @@ public class Reflection {
return new IllegalAccessException(msg);
}
+ /**
+ * Returns an IllegalAccessException with an exception message where
+ * there is no caller frame.
+ */
+ private static IllegalAccessException newIllegalAccessException(Class> memberClass,
+ int modifiers)
+ {
+ String memberSuffix = "";
+ Module m2 = memberClass.getModule();
+ if (m2.isNamed())
+ memberSuffix = " (in " + m2 + ")";
+
+ String memberPackageName = memberClass.getPackageName();
+
+ String msg = "JNI attached native thread (null caller frame) cannot access ";
+ if (m2.isExported(memberPackageName)) {
+
+ // module access okay so include the modifiers in the message
+ msg += "a member of " + memberClass + memberSuffix +
+ " with modifiers \"" + Modifier.toString(modifiers) + "\"";
+
+ } else {
+ // module access failed
+ msg += memberClass + memberSuffix+ " because "
+ + m2 + " does not export " + memberPackageName;
+ }
+
+ return new IllegalAccessException(msg);
+ }
+
/**
* Returns true if {@code currentClass} and {@code memberClass}
* are nestmates - that is, if they have the same nesthost as
diff --git a/test/jdk/java/lang/reflect/exeCallerAccessTest/CallerAccessTest.java b/test/jdk/java/lang/reflect/exeCallerAccessTest/CallerAccessTest.java
new file mode 100644
index 00000000000..873dabb73c0
--- /dev/null
+++ b/test/jdk/java/lang/reflect/exeCallerAccessTest/CallerAccessTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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
+ * @bug 8221530
+ * @summary Test uses custom launcher that starts VM using JNI that verifies
+ * reflection API with null caller class
+ * @library /test/lib
+ * @run main/native CallerAccessTest
+ */
+
+import java.io.File;
+import java.util.Map;
+import jdk.test.lib.Platform;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class CallerAccessTest {
+ public static void main(String[] args) throws IOException {
+ Path launcher = Paths.get(System.getProperty("test.nativepath"), "CallerAccessTest");
+ ProcessBuilder pb = new ProcessBuilder(launcher.toString());
+ Map