6839839: access checking logic is wrong at three points in MethodHandles
authorjrose
Tue, 12 May 2009 13:54:22 -0700
changeset 2764 2e45af54c0f9
parent 2763 172d44ffd1e5
child 2765 b17924394fab
6839839: access checking logic is wrong at three points in MethodHandles Summary: point fixes to access checking logic Reviewed-by: mr
jdk/src/share/classes/java/dyn/MethodHandles.java
jdk/src/share/classes/sun/dyn/DirectMethodHandle.java
jdk/src/share/classes/sun/dyn/MemberName.java
jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
jdk/src/share/classes/sun/dyn/MethodHandleNatives.java
jdk/src/share/classes/sun/dyn/util/VerifyAccess.java
--- a/jdk/src/share/classes/java/dyn/MethodHandles.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/java/dyn/MethodHandles.java	Tue May 12 13:54:22 2009 -0700
@@ -145,27 +145,30 @@
             this.lookupClass = lookupClass;
         }
 
+        private static final Class<?> PUBLIC_ONLY = sun.dyn.empty.Empty.class;
+
         /** Version of lookup which is trusted minimally.
          *  It can only be used to create method handles to
          *  publicly accessible members.
          */
-        public static final Lookup PUBLIC_LOOKUP = new Lookup(null);
+        public static final Lookup PUBLIC_LOOKUP = new Lookup(PUBLIC_ONLY);
 
         /** Package-private version of lookup which is trusted. */
-        static final Lookup IMPL_LOOKUP = new Lookup(Access.class);
+        static final Lookup IMPL_LOOKUP = new Lookup(null);
         static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }
 
         private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
-            if (lookupClass == null ||
-                lookupClass == Access.class ||
-                lookupClass.getName().startsWith("java.dyn."))
+            String name = lookupClass.getName();
+            if (name.startsWith("java.dyn.") || name.startsWith("sun.dyn."))
                 throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
         }
 
         @Override
         public String toString() {
+            if (lookupClass == PUBLIC_ONLY)
+                return "public";
             if (lookupClass == null)
-                return "public";
+                return "privileged";
             return lookupClass.getName();
         }
 
@@ -205,6 +208,13 @@
          * with the receiver type ({@code defc}) prepended.
          * The method and all its argument types must be accessible to the lookup class.
          * <p>
+         * (<em>BUG NOTE:</em> The type {@code Object} may be prepended instead
+         * of the receiver type, if the receiver type is not on the boot class path.
+         * This is due to a temporary JVM limitation, in which MethodHandle
+         * claims to be unable to access such classes.  To work around this
+         * bug, use {@code convertArguments} to normalize the type of the leading
+         * argument to a type on the boot class path, such as {@code Object}.)
+         * <p>
          * When called, the handle will treat the first argument as a receiver
          * and dispatch on the receiver's type to determine which method
          * implementation to enter.
@@ -253,8 +263,7 @@
             MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
             checkStatic(false, method, lookupClass);
             if (name.equals("<init>")) {
-                if (defc != specialCaller)
-                    throw newNoAccessException("constructor must be local to lookup class", method, lookupClass);
+                throw newNoAccessException("cannot directly invoke a constructor", method, null);
             } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
                 throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
             }
--- a/jdk/src/share/classes/sun/dyn/DirectMethodHandle.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/DirectMethodHandle.java	Tue May 12 13:54:22 2009 -0700
@@ -45,8 +45,6 @@
         if (!m.isResolved())
             throw new InternalError();
 
-        // Null check and replace privilege token (as passed to JVM) with null.
-        if (lookupClass.equals(Access.class))  lookupClass = null;
         MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
     }
 
--- a/jdk/src/share/classes/sun/dyn/MemberName.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/MemberName.java	Tue May 12 13:54:22 2009 -0700
@@ -450,7 +450,7 @@
             for (;;) {
                 int bufCount = MethodHandleNatives.getMembers(defc,
                         matchName, matchSig, matchFlags,
-                        MethodHandleNatives.asNativeCaller(lookupClass),
+                        lookupClass,
                         totalCount, buf);
                 if (bufCount <= buf.length) {
                     if (bufCount >= 0)
@@ -487,14 +487,13 @@
             return result;
         }
         boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
-            Class<?> caller = MethodHandleNatives.asNativeCaller(lookupClass);
-            MethodHandleNatives.resolve(m, caller);
+            MethodHandleNatives.resolve(m, lookupClass);
             if (m.isResolved())  return true;
             int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
             String matchSig = m.getSignature();
             MemberName[] buf = { m };
             int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
-                    m.getName(), matchSig, matchFlags, caller, 0, buf);
+                    m.getName(), matchSig, matchFlags, lookupClass, 0, buf);
             if (n != 1)  return false;
             return m.isResolved();
         }
--- a/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Tue May 12 13:54:22 2009 -0700
@@ -95,7 +95,7 @@
 
     public static void initLookup(Access token, Lookup lookup) {
         Access.check(token);
-        if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != Access.class)
+        if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != null)
             throw new InternalError();
         IMPL_LOOKUP_INIT = lookup;
     }
@@ -144,19 +144,28 @@
             boolean doDispatch, Class<?> lookupClass) {
         Access.check(token);  // only trusted calls
         MethodType mtype = method.getMethodType();
+        MethodType rtype = mtype;
         if (method.isStatic()) {
             doDispatch = false;
         } else {
             // adjust the advertised receiver type to be exactly the one requested
             // (in the case of invokespecial, this will be the calling class)
-            mtype = mtype.insertParameterType(0, method.getDeclaringClass());
+            Class<?> recvType = method.getDeclaringClass();
+            mtype = mtype.insertParameterType(0, recvType);
             if (method.isConstructor())
                 doDispatch = true;
+            // FIXME: JVM has trouble building MH.invoke sites for
+            // classes off the boot class path
+            rtype = mtype;
+            if (recvType.getClassLoader() != null)
+                rtype = rtype.changeParameterType(0, Object.class);
         }
         DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
         if (!mh.isValid())
             throw newNoAccessException(method, lookupClass);
-        return mh;
+        MethodHandle rmh = AdapterMethodHandle.makePairwiseConvert(token, rtype, mh);
+        if (rmh == null)  throw new InternalError();
+        return rmh;
     }
 
     public static
@@ -189,6 +198,15 @@
     MethodHandle bindReceiver(Access token,
                               MethodHandle target, Object receiver) {
         Access.check(token);
+        if (target instanceof AdapterMethodHandle) {
+            Object info = MethodHandleNatives.getTargetInfo(target);
+            if (info instanceof DirectMethodHandle) {
+                DirectMethodHandle dmh = (DirectMethodHandle) info;
+                if (receiver == null ||
+                    dmh.type().parameterType(0).isAssignableFrom(receiver.getClass()))
+                    target = dmh;
+            }
+        }
         if (target instanceof DirectMethodHandle)
             return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
         return null;   // let caller try something else
--- a/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java	Tue May 12 13:54:22 2009 -0700
@@ -47,14 +47,6 @@
     static native int getMembers(Class<?> defc, String matchName, String matchSig,
             int matchFlags, Class<?> caller, int skip, MemberName[] results);
 
-    static Class<?> asNativeCaller(Class<?> lookupClass) {
-        if (lookupClass == null)  // means "public only, non-privileged"
-            return sun.dyn.empty.Empty.class;
-        if (lookupClass == Access.class)  // means "internal, privileged"
-            return null;    // to the JVM, null means completely privileged
-        return lookupClass;
-    }
-
     /// MethodHandle support
 
     /** Initialize the method handle to adapt the call. */
--- a/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java	Mon May 11 21:09:58 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java	Tue May 12 13:54:22 2009 -0700
@@ -95,7 +95,7 @@
     public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
         if (class1 == class2)
             return true;
-        if (loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
+        if (!loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
             return false;
         String name1 = class1.getName(), name2 = class2.getName();
         int dot = name1.lastIndexOf('.');
@@ -159,7 +159,7 @@
      */
     public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass,
                                                String permissionName) {
-        if (requestingClass == Access.class)  return;
+        if (requestingClass == null)          return;
         if (requestingClass == subjectClass)  return;
         SecurityManager security = System.getSecurityManager();
         if (security == null)  return;  // open season