8177146: MethodHandles.Lookup::bind allows illegal protected access
Reviewed-by: psandoz, vlivanov, redestad
Contributed-by: ron.pressler@oracle.com
--- 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();
+ }
+}