8229785: MethodType::fromMethodDescriptorString should require security permission if loader is null
Reviewed-by: vromero
--- a/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java Tue Sep 10 10:24:42 2019 -0700
+++ b/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java Tue Sep 10 10:35:52 2019 -0700
@@ -26,6 +26,8 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.List;
@@ -132,7 +134,14 @@
@Override
public MethodType resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException {
- MethodType mtype = MethodType.fromMethodDescriptorString(descriptorString(), lookup.lookupClass().getClassLoader());
+ MethodType mtype = AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public MethodType run() {
+ return MethodType.fromMethodDescriptorString(descriptorString(),
+ lookup.lookupClass().getClassLoader());
+ }
+ });
+
// let's check that the lookup has access to all the types in the method type
lookup.accessClass(mtype.returnType());
for (Class<?> paramType: mtype.parameterArray()) {
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java Tue Sep 10 10:24:42 2019 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java Tue Sep 10 10:35:52 2019 -0700
@@ -46,9 +46,11 @@
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
+import sun.security.util.SecurityConstants;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
+import static java.lang.invoke.MethodType.fromDescriptor;
/**
* A method type represents the arguments and return type accepted and
@@ -1076,9 +1078,8 @@
/**
* Finds or creates an instance of a method type, given the spelling of its bytecode descriptor.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
- * Any class or interface name embedded in the descriptor string
- * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
- * on the given loader (or if it is null, on the system class loader).
+ * Any class or interface name embedded in the descriptor string will be
+ * resolved by the given loader (or if it is null, on the system class loader).
* <p>
* Note that it is possible to encounter method types which cannot be
* constructed by this method, because their component types are
@@ -1092,10 +1093,19 @@
* @throws NullPointerException if the string is null
* @throws IllegalArgumentException if the string is not well-formed
* @throws TypeNotPresentException if a named type cannot be found
+ * @throws SecurityException if the security manager is present and
+ * {@code loader} is {@code null} and the caller does not have the
+ * {@link RuntimePermission}{@code ("getClassLoader")}
*/
public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader)
throws IllegalArgumentException, TypeNotPresentException
{
+ if (loader == null) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+ }
+ }
return fromDescriptor(descriptor,
(loader == null) ? ClassLoader.getSystemClassLoader() : loader);
}
--- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Tue Sep 10 10:24:42 2019 -0700
+++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Tue Sep 10 10:35:52 2019 -0700
@@ -90,9 +90,7 @@
i[0] = endc+1;
String name = str.substring(begc, endc).replace('/', '.');
try {
- return (loader == null)
- ? Class.forName(name, false, null)
- : loader.loadClass(name);
+ return Class.forName(name, false, loader);
} catch (ClassNotFoundException ex) {
throw new TypeNotPresentException(name, ex);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/constant/methodTypeDesc/ResolveConstantDesc.java Tue Sep 10 10:35:52 2019 -0700
@@ -0,0 +1,107 @@
+/*
+ * 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 8229785
+ * @library /test/lib
+ * @build jdk.unsupported/*
+ * @summary MethodTypeDesc::resolveConstantDesc with security manager
+ * @run main/othervm/policy=test.policy ResolveConstantDesc
+ */
+
+import java.lang.constant.MethodTypeDesc;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.security.AccessControlException;;
+import java.security.Permission;;
+
+import static jdk.test.lib.Asserts.*;
+
+/*
+ * MethodTypeDesc::resolveConstantDec may get security exception depending
+ * on the access of Lookup object
+ */
+public class ResolveConstantDesc {
+ private static final String DESCRIPTOR = "()Ljdk/internal/misc/VM;";
+
+ public static void main(String... args) throws Exception {
+ // private Lookup object has access to classes exported from another module
+ Lookup lookup = sun.misc.Test.LOOKUP;
+ Module m = lookup.lookupClass().getModule();
+
+ MethodType mtype = MethodType.fromMethodDescriptorString(DESCRIPTOR, ClassLoader.getPlatformClassLoader());
+ Class<?> target = mtype.returnType();
+ Module javaBase = target.getModule();
+ assertTrue(javaBase.isExported(target.getPackageName(), m));
+
+ // MethodType that references java.base internal class
+ MethodTypeDesc mtd = MethodTypeDesc.ofDescriptor(DESCRIPTOR);
+ testInaccessibleClass(mtd);
+
+ // Lookup has no access to JDK internal API; IAE
+ throwIAE(MethodHandles.lookup(), mtd);
+
+ // resolve successfully if Lookup has access to sun.misc and security permission
+ MethodTypeDesc.ofDescriptor("()Lsun/misc/Unsafe;")
+ .resolveConstantDesc(MethodHandles.lookup());
+ }
+
+ /*
+ * Test Lookup with different access
+ */
+ private static void testInaccessibleClass(MethodTypeDesc mtd) throws Exception {
+ Lookup lookup = sun.misc.Test.LOOKUP;
+ // full power lookup can resolve MethodTypeDesc of java.base internal types
+ mtd.resolveConstantDesc(lookup);
+
+ // drop PRIVATE access; fail package access check
+ throwACC(lookup.dropLookupMode(Lookup.PRIVATE), mtd);
+
+ // jdk.internal.access is not accessible by jdk.unsupported
+ MethodTypeDesc mtd1 = MethodTypeDesc.ofDescriptor("()Ljdk/internal/access/SharedSecrets;");
+ throwIAE(lookup, mtd1);
+ }
+
+ // IAE thrown when resolving MethodType using the given Lookup object
+ private static void throwIAE(Lookup lookup, MethodTypeDesc mtd) throws Exception {
+ try {
+ MethodType mtype = (MethodType)mtd.resolveConstantDesc(lookup);
+ throw new RuntimeException("unexpected IAE not thrown");
+ } catch (IllegalAccessException e) { }
+ }
+
+ private static void throwACC(Lookup lookup, MethodTypeDesc mtd) throws Exception {
+ try {
+ MethodType mtype = (MethodType)mtd.resolveConstantDesc(lookup);
+ throw new RuntimeException("unexpected IAE not thrown");
+ } catch (AccessControlException e) {
+ Permission perm = e.getPermission();
+ if (!(perm instanceof RuntimePermission &&
+ "accessClassInPackage.jdk.internal.misc".equals(perm.getName()))) {
+ throw e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/constant/methodTypeDesc/jdk.unsupported/sun/misc/Test.java Tue Sep 10 10:35:52 2019 -0700
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package sun.misc;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class Test {
+ public static final Lookup LOOKUP = MethodHandles.lookup();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/constant/methodTypeDesc/test.policy Tue Sep 10 10:35:52 2019 -0700
@@ -0,0 +1,6 @@
+// security policy for ResolveConstantDesc test
+grant {
+ permission java.lang.RuntimePermission "getClassLoader";
+ permission "java.lang.RuntimePermission" "accessClassInPackage.sun.misc";
+};
+
--- a/test/jdk/java/lang/invoke/FindClassSecurityManager.java Tue Sep 10 10:24:42 2019 -0700
+++ b/test/jdk/java/lang/invoke/FindClassSecurityManager.java Tue Sep 10 10:35:52 2019 -0700
@@ -25,7 +25,7 @@
/* @test
* @bug 8139885
- * @run main/othervm/policy=findclass.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager
+ * @run main/othervm/policy=getclassloader.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager
*/
package test.java.lang.invoke;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/MethodTypeSecurityManager.java Tue Sep 10 10:35:52 2019 -0700
@@ -0,0 +1,79 @@
+/*
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 8229785
+ * @summary Test MethodType.fromMethodDescriptorString with security manager
+ * @run main/othervm test.java.lang.invoke.MethodTypeSecurityManager
+ * @run main/othervm/policy=getclassloader.policy test.java.lang.invoke.MethodTypeSecurityManager access
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodType;
+import java.security.AccessControlException;
+import java.security.Permission;
+
+public class MethodTypeSecurityManager {
+ private static boolean hasClassLoaderAccess;
+ public static void main(String... args) throws Throwable {
+ ClassLoader platformLoader = ClassLoader.getPlatformClassLoader();
+ ClassLoader appLoader = ClassLoader.getSystemClassLoader();
+ hasClassLoaderAccess = args.length == 1 && "access".equals(args[0]);
+
+ assert hasClassLoaderAccess || System.getSecurityManager() == null;
+ if (!hasClassLoaderAccess) {
+ System.setSecurityManager(new SecurityManager());
+ }
+
+ // require getClassLoader permission
+ throwACC("()Ljdk/internal/misc/VM;", null);
+ // package access check when app class loader loads the class
+ throwACC("()Ljdk/internal/misc/VM;", appLoader);
+
+ // if using the platform class loader, no package access check
+ MethodType.fromMethodDescriptorString("()Ljdk/internal/misc/VM;", platformLoader);
+ }
+
+ private static void throwACC(String desc, ClassLoader loader) {
+ try {
+ MethodType.fromMethodDescriptorString(desc, loader);
+ throw new RuntimeException("should never leak JDK internal class");
+ } catch (AccessControlException e) {
+ System.out.println(e.getMessage());
+ Permission perm = e.getPermission();
+ if (!(perm instanceof RuntimePermission)) throw e;
+ // ACC thrown either no "getClassLoader" permission or no package access
+ switch (perm.getName()) {
+ case "getClassLoader":
+ if (!hasClassLoaderAccess) break;
+ case "accessClassInPackage.jdk.internal.misc":
+ break;
+ default:
+ throw e;
+ }
+ }
+ }
+}
--- a/test/jdk/java/lang/invoke/findclass.security.policy Tue Sep 10 10:24:42 2019 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-/*
- * Security policy used by the FindClassSecurityManager test.
- * Must allow file reads so that jtreg itself can run, and getting class loaders.
- */
-
-grant {
- permission java.io.FilePermission "*", "read";
- permission java.lang.RuntimePermission "getClassLoader";
-};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/getclassloader.policy Tue Sep 10 10:35:52 2019 -0700
@@ -0,0 +1,10 @@
+/*
+ * Security policy used by the FindClassSecurityManager and
+ * MethodTypeSecurityManager test.
+ * Must allow file reads so that jtreg itself can run, and getting class loaders.
+ */
+
+grant {
+ permission java.io.FilePermission "*", "read";
+ permission java.lang.RuntimePermission "getClassLoader";
+};