MethodHandles.publicLookup().in() returns Lookup with no permissions?

Mandy Chung mandy.chung at oracle.com
Tue Nov 24 19:49:30 UTC 2020



On 11/24/20 11:21 AM, Simone Bordet wrote:
> Hi,
>
> On Tue, Nov 24, 2020 at 7:35 PM Alan Bateman <Alan.Bateman at oracle.com> wrote:
>> On 24/11/2020 18:21, Simone Bordet wrote:
>>> Hi,
>>>
>>> testing the Jetty MethodHandle usages we encountered this situation:
>>>
>>> .class org.openjdk.mh.Main
>>> ----
>>> Class<?> klass = Main.class;
>>> MethodHandles.Lookup lookup = MethodHandles.publicLookup().in(klass);
>>> MethodHandle handle = lookup.findVirtual(klass, "test",
>>> MethodType.methodType(String.class));
>>> ----
>>>
>>> .module-info.java
>>> ----
>>> module org.openjdk.mh {
>>>       exports org.openjdk.mh to com.acme;
>>> }
>>> ----
>>>
>>> findVirtual() throws:
>>> java.lang.IllegalAccessException: symbolic reference class is not
>>> accessible: class org.openjdk.mh.Main, from
>>> org.openjdk.mh.Main/noaccess (module org.openjdk.mh)
>>>
>>> Removing the "to" clause from the exports in module-info.java fixes the issue.
>>>
>>> Seems that a class cannot get a MethodHandle on itself, the reason
>>> being that org.openjdk.mh is selectively exported in module-info.java.
>>>
>>> Is this intended behavior?
>>> I would have expected that a class could always get a MethodHandle on itself.
>>> I tested this with 11, 15, and 16+25, all fail.
>>>
>>> I have a simple reproducer, and I can open an OpenJDK bug.
>>>
>> It looks like you are starting with publicLookup and then teleporting to
>> mh.Main that is not accessible to that lookup. When you change it from a
>> qualified export  to exporting it to all modules then it becomes
>> accessible to the lookup so that is why it works.
> The "publicLookup()" should give (minimal) access to public classes
> and public methods.
>
> We want this minimal access requirement because Jetty creates a
> MethodHandle on classes that are typically written by users.
> We don't want Jetty to require that users classes are "opened" (in
> JPMS meaning) because we only need to access public classes and public
> methods.
>
> The problem stems while trying to run our own test cases that happen
> to be in the same module (test classes are patched to main classes of
> the module).
> In this case, the "user class" is a test class in the same package and
> module as the main classes which, differently from real "user
> classes", have a more restricted JPMS exports clause.
>
> It broke my least surprise principle that a class cannot get a
> MethodHandle on one of its own public methods using publicLookup() --
> I assumed a class is implicitly exported to its own module, so why
> would it not work?

publicLookup allows you to access public unconditionally-exported classes.
public lookup has no lookup class as the context (as specified in the 
javadoc that publicLookup.in(C.class) can teleport to another public 
lookup on C but no change in its access - still can access all public 
unconditionally-exported classes).

In addition, findVirtual and other method handle lookup operations are 
*not* caller-sensitive but instead the access check is based on the 
lookup context.

MethodHandles::lookup has full-privilege access but it does not leak any 
access as long as the Lookup object is not leaked to other 
classes/modules accidentally.

You want to access public unconditionally exported user classes.  I 
think the test should unconditionally exported its classes in the same 
way as user classes, shouldn't it?


>> Have you tried starting with MethodHandles.lookup() ?
> I do and it solves the issue at hand, but it's not the right way to
> obtain the Lookup in our case because it requires more access than
> needed to user classes (they must open themselves to Jetty
> implementation modules).
>
> I'd like to gently push on "why cannot a publicLookup().in(klass) work
> on a public method of klass itself?".
>
> Seems straightforward that it should, independently of exports
> declarations, because it's a class "reflecting" on itself.

To detect if a class is in the same module as the caller, the API will 
need to be caller-sensitive which should be carefully considered [1].  
What you are proposing is that Lookup::in should be caller-sensitive 
that is against the Lookup API design model that does the access check 
based on the lookup context but not the caller.

Mandy
https://openjdk.java.net/jeps/176

> Thanks for bearing with my gentle push :)
>



More information about the jigsaw-dev mailing list