8229785: MethodType::fromMethodDescriptorString should require security permission if loader is null
authormchung
Tue, 10 Sep 2019 10:35:52 -0700
changeset 58078 41f119856e7c
parent 58077 874edfe72c3e
child 58079 8db87a43a1ce
8229785: MethodType::fromMethodDescriptorString should require security permission if loader is null Reviewed-by: vromero
src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java
src/java.base/share/classes/java/lang/invoke/MethodType.java
src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java
test/jdk/java/lang/constant/methodTypeDesc/ResolveConstantDesc.java
test/jdk/java/lang/constant/methodTypeDesc/jdk.unsupported/sun/misc/Test.java
test/jdk/java/lang/constant/methodTypeDesc/test.policy
test/jdk/java/lang/invoke/FindClassSecurityManager.java
test/jdk/java/lang/invoke/MethodTypeSecurityManager.java
test/jdk/java/lang/invoke/findclass.security.policy
test/jdk/java/lang/invoke/getclassloader.policy
--- 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";
+};