From 5fbf4e8b326dd8453822ae40f3c6e4adffbf42ac Mon Sep 17 00:00:00 2001
From: zhangyipeng  <zhangyipeng7@huawei.com>
Date: Fri, 3 Nov 2023 16:21:09 +0800
Subject: [PATCH] [Backport]8177146: MethodHandles.Lookup::bind allows illegal protected
 access

---
 .../classes/java/lang/invoke/MethodHandles.java    | 22 +++--
 .../lang/invoke/8177146/TestMethodHandleBind.java  | 96 ++++++++++++++++++++++
 jdk/test/java/lang/invoke/8177146/pkg/A.java       | 40 +++++++++
 3 files changed, 149 insertions(+), 9 deletions(-)
 create mode 100644 jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java
 create mode 100644 jdk/test/java/lang/invoke/8177146/pkg/A.java

diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
index 7b9353ab0..62888c019 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
@@ -1148,7 +1148,13 @@ return mh1;
         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);
         }
 
@@ -1591,7 +1597,7 @@ return mh1;
                 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));
@@ -1604,11 +1610,11 @@ return mh1;
             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 {
@@ -1660,10 +1666,8 @@ return mh1;
             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);
diff --git a/jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java b/jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java
new file mode 100644
index 000000000..134cc9f75
--- /dev/null
+++ b/jdk/test/java/lang/invoke/8177146/TestMethodHandleBind.java
@@ -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) {
+        }
+    }
+}
diff --git a/jdk/test/java/lang/invoke/8177146/pkg/A.java b/jdk/test/java/lang/invoke/8177146/pkg/A.java
new file mode 100644
index 000000000..f34d52b8e
--- /dev/null
+++ b/jdk/test/java/lang/invoke/8177146/pkg/A.java
@@ -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();
+    }
+}
-- 
2.12.3

