changeset 6815:ad8490675e4c

8021368: Launch of Java Web Start app fails with ClassCircularityError exception in 7u25 Reviewed-by: alanb, plevart, jfranck
author mchung
date Wed, 18 Dec 2013 16:45:18 -0800
parents fa00be1579b8
children cd2d8f23e93c
files make/java/java/mapfile-vers src/share/classes/java/lang/Class.java src/share/native/java/lang/Class.c test/java/lang/Class/checkMemberAccess/CheckMemberAccess.java test/java/lang/Class/checkMemberAccess/test.policy
diffstat 5 files changed, 187 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/java/mapfile-vers	Fri Dec 13 16:11:15 2013 -0800
+++ b/make/java/java/mapfile-vers	Wed Dec 18 16:45:18 2013 -0800
@@ -121,6 +121,7 @@
 		Java_java_io_UnixFileSystem_setReadOnly;
 		Java_java_io_UnixFileSystem_setPermission;
 		Java_java_lang_Class_forName0;
+                Java_java_lang_Class_getCheckMemberAccessMethod;
 		Java_java_lang_Class_getPrimitiveClass;
 		Java_java_lang_Class_isAssignableFrom;
 		Java_java_lang_Class_isInstance;
--- a/src/share/classes/java/lang/Class.java	Fri Dec 13 16:11:15 2013 -0800
+++ b/src/share/classes/java/lang/Class.java	Wed Dec 18 16:45:18 2013 -0800
@@ -2207,14 +2207,53 @@
      */
     static native Class getPrimitiveClass(String name);
 
-    private static boolean isCheckMemberAccessOverridden(SecurityManager smgr) {
-        if (smgr.getClass() == SecurityManager.class) return false;
+    private static class SecurityManagerHelper {
+        final SecurityManager sm;
+        final boolean overrideCheckMemberAccess;
+        SecurityManagerHelper(SecurityManager sm) {
+            this.sm = sm;
 
-        Class<?>[] paramTypes = new Class<?>[] {Class.class, int.class};
-        return smgr.getClass().getMethod0("checkMemberAccess", paramTypes).
-                getDeclaringClass() != SecurityManager.class;
+            boolean overridden = false;
+            if (sm.getClass() != SecurityManager.class) {
+                try {
+                    overridden = getCheckMemberAccessMethod(sm.getClass()).
+                                     getDeclaringClass() != SecurityManager.class;
+                } catch (NoSuchMethodError e) {
+                    // fall back to invoke sm.checkMemberAccess for the member access check
+                }
+            }
+            this.overrideCheckMemberAccess = overridden;
+        }
+
     }
 
+    private static volatile SecurityManagerHelper smHelper;
+    private static boolean isCheckMemberAccessOverridden(SecurityManager sm) {
+        if (sm.getClass() == SecurityManager.class)  return false;
+
+        SecurityManagerHelper helper = smHelper;
+        if (helper == null || helper.sm != sm) {
+            helper = new SecurityManagerHelper(sm);
+            smHelper = helper;
+        }
+        return helper.overrideCheckMemberAccess;
+    }
+
+    /**
+     * Finds the checkMemberAccess method of the given SecurityManager class.
+     *
+     * This method calls JNI_GetMethodID to look up the checkMemberAccess method
+     * instead of Class.getMethod0 that may cause loading of classes referenced
+     * by the SecurityManager subclass and cause ClassCircularityError.
+     *
+     * JNI_GetMethodID may throw NoSuchMethodError if the given class
+     * has a static checkMemberAccess method.
+     *
+     * @throws NoSuchMethodError if the method cannot be found.
+     */
+    private static native Method getCheckMemberAccessMethod(Class<? extends SecurityManager> c)
+        throws NoSuchMethodError;
+
 
     /*
      * Check if client is allowed to access members.  If access is denied,
--- a/src/share/native/java/lang/Class.c	Fri Dec 13 16:11:15 2013 -0800
+++ b/src/share/native/java/lang/Class.c	Wed Dec 18 16:45:18 2013 -0800
@@ -187,3 +187,16 @@
 
     return result;
 }
+
+JNIEXPORT jobject JNICALL
+Java_java_lang_Class_getCheckMemberAccessMethod(JNIEnv *env, jclass cls, jclass smClass)
+{
+    jmethodID mid;
+
+    mid = (*env)->GetMethodID(env, smClass, "checkMemberAccess", "(Ljava/lang/Class;I)V");
+    if (mid == NULL) {
+        return NULL;
+    }
+    return (*env)->ToReflectedMethod(env, smClass, mid, JNI_FALSE);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/Class/checkMemberAccess/CheckMemberAccess.java	Wed Dec 18 16:45:18 2013 -0800
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013, 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 java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/*
+ * @test
+ * @bug 8021368
+ * @summary SecurityManager.checkMemberAccess call should not resolve
+ *          and load other classes
+ * @run main/othervm/policy=test.policy CheckMemberAccess
+ */
+
+public class CheckMemberAccess {
+    private static int count = 0;
+    public static void main(String[] args) throws Exception {
+        String testClasses = System.getProperty("test.classes", ".");
+        // remove Foo class
+        // the test will verify SecurityManager.checkMemberAccess should not
+        // cause any class loading of implementation classes
+        Path p = Paths.get(testClasses, "CheckMemberAccess$Foo.class");
+        if (Files.exists(p)) {
+            // Foo already deleted in rerun
+            Files.delete(p);
+        }
+        // patch the checkMemberAcces_ method name
+        patch(Paths.get(testClasses, "CheckMemberAccess$PrivateCheckMemberAccess.class"));
+        patch(Paths.get(testClasses, "CheckMemberAccess$StaticCheckMemberAccess.class"));
+
+        test(new OverriddedCheckMemberAccess(), count+1);
+        test(new NoOverriddedCheckMemberAccess(), count+1);
+        test(new PrivateCheckMemberAccess(), count);
+        test(new StaticCheckMemberAccess(), count);
+    }
+
+    private static void patch(Path p) throws IOException {
+        // s/checkMemberAcces_/checkMemberAccess
+        byte[] bytes = Files.readAllBytes(p);
+        int len = "Acces_".length();
+        for (int i=0; i < bytes.length-len; i++) {
+            if (bytes[i] == 'A' &&
+                bytes[i+1] == 'c' &&
+                bytes[i+2] == 'c' &&
+                bytes[i+3] == 'e' &&
+                bytes[i+4] == 's' &&
+                bytes[i+5] == '_') {
+                bytes[i+5] = 's';
+                break;
+            }
+        }
+        Files.write(p, bytes);
+    }
+
+    public void findMe() {};
+    public static void test(SecurityManager smgr, int expected) throws Exception {
+        System.setSecurityManager(smgr);
+        // this will trigger SecurityManager.checkMemberAccess to be called
+        Method m = CheckMemberAccess.class.getMethod("findMe", new Class<?>[0]);
+        if (count != expected) {
+            throw new RuntimeException(smgr.getClass() + ": " + count + " != " + expected);
+        }
+    }
+
+    static class OverriddedCheckMemberAccess extends SecurityManager {
+        @Override
+        public void checkMemberAccess(Class<?> clazz, int which) {
+            System.out.println("OverriddedCheckMemberAccess.checkMemberAccess called");
+            count++;
+        }
+        // implementation-specific class should not be loaded when
+        // this.checkMemberAccess is called
+        public Foo foo() {
+            return null;
+        }
+    }
+    static class NoOverriddedCheckMemberAccess extends OverriddedCheckMemberAccess {
+    }
+    static class PrivateCheckMemberAccess extends SecurityManager {
+        private void checkMemberAcces_(Class<?> clazz, int which) {
+            throw new RuntimeException("should not reach here");
+        }
+        // implementation-specific class should not be loaded when
+        // this.checkMemberAccess is called
+        public Foo foo() {
+            return null;
+        }
+    }
+    static class StaticCheckMemberAccess extends SecurityManager {
+        public static void checkMemberAcces_(Class<?> clazz, int which) {
+            throw new RuntimeException("should not reach here");
+        }
+        // implementation-specific class should not be loaded when
+        // this.checkMemberAccess is called
+        public Foo foo() {
+            return null;
+        }
+    }
+    static class Foo {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/Class/checkMemberAccess/test.policy	Wed Dec 18 16:45:18 2013 -0800
@@ -0,0 +1,6 @@
+grant {
+    permission java.lang.RuntimePermission "createSecurityManager";
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.io.FilePermission "<<ALL FILES>>", "read,write,delete";
+    permission java.util.PropertyPermission "*", "read";
+};