JDK-8172815 : MethodHandles.Lookup.revealDirect performs access checking against wrong class
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2017-01-13
  • Updated: 2018-09-27
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.
Other
tbdUnresolved
Related Reports
Blocks :  
Relates :  
Relates :  
Description
The lookup factory methods of MethodHandles.Lookup perform access checking based on a provided "referenced class", 'refc'. However, the 'revealDirect' method checks access against the *declaring* class. As a result, a straightforward back-to-back findStatic/revealDirect can fail.

---

package p1;
class A {
    public static void staticMethod() {}
    public void instanceMethod() {}
}

---

package p1;
public class B extends A {}

---

package p2;
public class C extends p1.B {}

---

import java.lang.invoke.*;

public class Test {
    public static void main(String... args) throws Exception {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findStatic(p2.C.class, "staticMethod", MethodType.methodType(void.class));
        System.out.println("mh: " + mh);
        MethodHandleInfo mhi = lookup.revealDirect(mh);
        System.out.println("mhi: " + mhi);
    }
}

---

Program output:

mh: MethodHandle()void
Exception in thread "main" java.lang.IllegalArgumentException: java.lang.IllegalAccessException: class is not public: p1.A.staticMethod()void/invokeStatic, from Test (unnamed module @3834d63f)
	at java.base/java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1835)
	at Test.main(Test.java:10)
Caused by: java.lang.IllegalAccessException: class is not public: p1.A.staticMethod()void/invokeStatic, from Test (unnamed module @3834d63f)
	at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:915)
	at java.base/java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:2022)
	at java.base/java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1832)
	... 1 more

---

If I use 'findVirtual' to inspect 'instanceMethod' instead, there is no access checking error, although the result doesn't provide accurate information, apparently in an attempt to workaround the access checking problem:

mh: MethodHandle(C)void
mhi: invokeVirtual p1.B.instanceMethod:()void

(Note that p1.B is neither the declaring class nor the referenced class.)

---

This issue is closely tied to JDK-8068253: direct method handles ought to be preserving their referenced class rather than assuming the declaring class is sufficient.

Note that, because of this problem, LambdaMetafactory is unable to properly handle some inputs (such as for 'p2.C::staticMethod'), and as a workaround javac currently generates "bridge" lambda methods (see, e.g., JDK-8068254).
Comments
The DirectMethodHandle holds an instance of a resolved MemberName. That instance will contain the "declaring class". Resolution occurs via the native method MethodHandleNatives::resolve and the referring class is replaced with the "declaring class", thus all this happens in the HotSpot linking code (see methodHandles.cpp and linkResolver.cpp), and i think implementation specifics are leaking into DirectMethodHandle. - It would be unfortunately to have to add another field to DirectMethodHandle or MemberName. - It's not entirely clear if the information obtained from cracking a DirectMethodHandle instance is guaranteed to support full fidelity of the referencing class passed to look up the DirectMethodHandle instance. - A possible work around is to report the nearest public member as for invokevirtual, although that might have knock on effects in other places. (Note that for invokevirtual the referring class can be obtained from the first parameter type of the direct method handle's method type, although IIRC for protected methods there may be a narrowing of the type.)
24-01-2017