JDK-8235351 : Lookup::unreflect should bind with the original caller independent of Method's accessible flag
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9,11,14
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2019-11-29
  • Updated: 2021-02-10
  • Resolved: 2019-12-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 14
11.0.11-oracleFixed 14 b27Fixed
Description
A DESCRIPTION OF THE PROBLEM :
The problem is that a public @CallerSensitive methods can be tricked to see MethodHandle as caller. This can be done by getting the Method, call setAccessible(true) on it. Since it is public this call will succeed. Using this Method now in Lookup.unreflect will use the IMPL_LOOKUP, since the Method is accessible and does therefore not bind the caller. Calling the resulting MethodHandle via invokeWithArguments, the Method will see MethodHandle as caller. This can be used to open java.base.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run:

Method m = Module.class.getMethod("addOpens", String.class, Module.class);
m.setAccessible(true);
MethodHandle mh = MethodHandles.lookup().unreflect(m);
mh.invokeWithArguments(Object.class.getModule(), "java.lang", Test.class.getModule());

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Exception in thread "main" java.lang.IllegalCallerException: java.lang is not open to module test.
ACTUAL -
Module java.lang is opened to module test.

---------- BEGIN SOURCE ----------
package test;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;

public class Test {

	public static void main(String[] args) throws Throwable {
		Method m = Module.class.getMethod("addOpens", String.class, Module.class);
		m.setAccessible(true);
		MethodHandle mh = MethodHandles.lookup().unreflect(m);
		
		mh.invokeWithArguments(Object.class.getModule(), "java.lang", Test.class.getModule());
	}
	
}

---------- END SOURCE ----------

FREQUENCY : always



Comments
Fix Request Should get backported for parity with 11.0.11-oracle. Doesn't applies cleanly. Small adaptation needed. Review thread: http://mail.openjdk.java.net/pipermail/jdk-updates-dev/2020-December/004467.html
21-12-2020

URL: https://hg.openjdk.java.net/jdk/jdk/rev/4437d58547ce User: mchung Date: 2019-12-06 23:10:56 +0000
06-12-2019

Lookup::unreflect should bind MethodHandle with the original lookup class if it's a caller-sensitive method independent of the accessible flag. The test case only passes when running in the absence of the security manager. It will fail with SecurityException when running with a security manager: Exception in thread "main" java.security.AccessControlException: access denied ("java.lang.reflect.ReflectPermission" "suppressAccessChecks") at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) at java.base/java.security.AccessController.checkPermission(AccessController.java:1036) at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:408) at java.base/java.lang.reflect.AccessibleObject.checkPermission(AccessibleObject.java:87) at java.base/java.lang.reflect.Method.setAccessible(Method.java:191) at test.Test.main(Test.java:13)
05-12-2019