8177146: MethodHandles.Lookup::bind allows illegal protected access
authorpsandoz
Mon, 01 May 2017 10:43:40 -0700
changeset 44859 5775b131af51
parent 44858 7183899b064b
child 44860 a33107d3a388
8177146: MethodHandles.Lookup::bind allows illegal protected access Reviewed-by: psandoz, vlivanov, redestad Contributed-by: ron.pressler@oracle.com
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java
jdk/test/java/lang/invoke/8177146/pkg/A.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon May 01 07:33:19 2017 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon May 01 10:43:40 2017 -0700
@@ -1691,7 +1691,13 @@
         public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             Class<? extends Object> refc = receiver.getClass(); // may get NPE
             MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
-            MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
+            MethodHandle mh = getDirectMethodNoRestrictInvokeSpecial(refc, method, findBoundCallerClass(method));
+            if (!mh.type().leadingReferenceParameter().isAssignableFrom(receiver.getClass())) {
+                throw new IllegalAccessException("The restricted defining class " +
+                                                 mh.type().leadingReferenceParameter().getName() +
+                                                 " is not assignable from receiver class " +
+                                                 receiver.getClass().getName());
+            }
             return mh.bindArgumentL(0, receiver).setVarargs(method);
         }
 
@@ -2240,7 +2246,7 @@
                 throw method.makeAccessException("caller class must be a subclass below the method", caller);
             }
             MethodType rawType = mh.type();
-            if (rawType.parameterType(0) == caller)  return mh;
+            if (caller.isAssignableFrom(rawType.parameterType(0))) return mh; // no need to restrict; already narrow
             MethodType narrowType = rawType.changeParameterType(0, caller);
             assert(!mh.isVarargsCollector());  // viewAsType will lose varargs-ness
             assert(mh.viewAsTypeChecks(narrowType, true));
@@ -2253,11 +2259,11 @@
             final boolean checkSecurity = true;
             return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
         }
-        /** Check access and get the requested method, eliding receiver narrowing rules. */
-        private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
+        /** Check access and get the requested method, for invokespecial with no restriction on the application of narrowing rules. */
+        private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
             final boolean doRestrict    = false;
             final boolean checkSecurity = true;
-            return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
+            return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, callerClass);
         }
         /** Check access and get the requested method, eliding security manager checks. */
         private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
@@ -2309,10 +2315,8 @@
             DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
             MethodHandle mh = dmh;
             // Optionally narrow the receiver argument to refc using restrictReceiver.
-            if (doRestrict &&
-                   (refKind == REF_invokeSpecial ||
-                       (MethodHandleNatives.refKindHasReceiver(refKind) &&
-                           restrictProtectedReceiver(method)))) {
+            if ((doRestrict && refKind == REF_invokeSpecial) ||
+                    (MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(method))) {
                 mh = restrictReceiver(method, dmh, lookupClass());
             }
             mh = maybeBindCaller(method, mh, callerClass);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java	Mon May 01 10:43:40 2017 -0700
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017, 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 8177146
+ * @run testng/othervm TestMethodHandleBind
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+import static java.lang.invoke.MethodHandles.lookup;
+
+import static org.testng.Assert.*;
+
+public class TestMethodHandleBind extends pkg.A {
+    static class B extends TestMethodHandleBind {}
+
+    @Test
+    public void testInstanceOfCallerClass() throws Throwable {
+        MethodHandle bound = lookup().bind(new TestMethodHandleBind() , "m1", MethodType.methodType(String.class));
+        String x = (String)bound.invoke();
+        assertEquals(x, this.getClass().getSimpleName());
+    }
+
+    @Test
+    public void testInstanceOfCallerSubclass() throws Throwable {
+        MethodHandle bound = lookup().bind(new B() , "m1", MethodType.methodType(String.class));
+        // MethodHandle bound = lookup().findVirtual(B.class,  "m1", MethodType.methodType(String.class)).bindTo(new B());
+        String x = (String)bound.invoke();
+        assertEquals(x, "B");
+    }
+
+    @Test
+    public void testInstanceOfReceiverClass() throws Throwable {
+        try {
+            MethodHandle bound = lookup().bind(new pkg.A() , "m1", MethodType.methodType(String.class));
+            bound.invoke();
+            fail("IllegalAccessException expected");
+        } catch (IllegalAccessException e) {
+        }
+    }
+
+    @Test
+    public void testPublicMethod() throws Throwable {
+        MethodHandle bound = lookup().bind(new pkg.A() , "m2", MethodType.methodType(String.class));
+        String x = (String)bound.invoke();
+        assertEquals(x, "A");
+    }
+
+    @Test
+    public void testPublicMethod2() throws Throwable {
+        MethodHandle bound = lookup().bind(new TestMethodHandleBind(), "m2", MethodType.methodType(String.class));
+        String x = (String)bound.invoke();
+        assertEquals(x, this.getClass().getSimpleName());
+    }
+
+    @Test
+    public void testInstanceOfCallerClassVarargs() throws Throwable {
+        MethodHandle bound = lookup().bind(new TestMethodHandleBind() , "m3", MethodType.methodType(String.class, String[].class));
+        String x = (String)bound.invoke("a", "b", "c");
+        assertEquals(x, this.getClass().getSimpleName() + "abc");
+    }
+
+    @Test
+    public void testInstanceOfReceiverClassVarargs() throws Throwable {
+        try {
+            MethodHandle bound = lookup().bind(new pkg.A(), "m3", MethodType.methodType(String.class, String[].class));
+            bound.invoke();
+            fail("IllegalAccessException expected");
+        } catch (IllegalAccessException e) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/8177146/pkg/A.java	Mon May 01 10:43:40 2017 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017, 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 pkg;
+
+public class A {
+    protected String m1() {
+        return this.getClass().getSimpleName();
+    }
+
+    public String m2() {
+        return this.getClass().getSimpleName();
+    }
+
+    protected String m3(String... args) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : args)
+            sb.append(s);
+        return this.getClass().getSimpleName() + sb.toString();
+    }
+}