Re: JDK Dynalink only works with unconditionally exported packages

2021-05-11 Thread Attila Szegedi
On 2021. May 11., at 1:51, Thiago Henrique Hupner  wrote:
> 
> Hi all.
> 
> I've been testing the JDK Dynalink recently and
> I think I've found a bug.
> 
> The class jdk.dynalink.beans.CheckRestrictedPackage checks
> if a package is restricted.
> 
> So, I have a class that has some static methods. But to Dynalink find the
> class,
> the class needs to be public. OK,
> I've changed my class to be public. However, it needs that
> the package of the class is exported in the module too.
> 
> I think this is a little too restrictive. I don't want to expose
> to my users some internal implementation.

The module exports is exactly the feature used to regulate what are you 
exposing from the module. In this case, you could have a class in a different 
package exposing those static methods (even if only just delegating to the 
original unexported class), and export those methods. 

Dynalink’s BeansLinker deliberately implemented in such a manner that it only 
allows access to entirely unrestricted classes. So what you’re experiencing is 
not a bug, it is the expected behavior. It’s admittedly a compromise between a 
simpler security model vs. losing some power.

> 
> The main problem is that the method
> CheckRestrictedPackage::isRestrictedClass
> does the following check:
> 
> // ...
> final String pkgName = name.substring(0, i);
> final Module module = clazz.getModule();
> if (module != null && !module.isExported(pkgName)) {
>return true;
> }
> // ...
> 
> I have a feeling that this check should be changed to something like
> 
> // ...
> final String pkgName = name.substring(0, i);
> final Module module = clazz.getModule();
> if (module != null && !module.isOpen(pkgName,
> CheckRestrictedPackage.class.getModule())) {
>return true;
> }
> // ...
> 
> In this way, my code can only "opens" a single package to the jdk.dynalink
> module,
> not having to unconditionally export a package.

If we did just what you suggested, then every package you’d open to 
jdk.dynalink would become wide open accessible to everyone through Dynalink. 
That’d obviously be a problem as it’d allow circumventing module visibility 
rules.

That would be fixable with some more work, though. It would be possible to 
create an enhancement where Dynalink allows linking to methods of 
non-globally-exported classes if it’d verify each such link attempt through 
sun.invoke.util.VerifyAccess.isMemberAccessible, using the data from the 
MethodHandle.Lookup object passed in LinkRequest. These lookup objects 
typically originate from the bootstrap methods for invokedynamic call sites in 
the target class.

Then you’d also indeed need to ensure that Dynalink can reflect on those 
packages, so you’d still have to open the package towards Dynalink, but 
Dynalink itself would enforce that it itself only allows linking call sites 
according to JVM linkage rules. 

This is not very simple to implement though, as the isRestrictedClass is used 
for various purposes right now, and would need to be replaced with something 
more granular, and then calls to VerifyAccess.isMemberAccessible would be 
needed to be added into the logic for resolving property getters and setters 
and method getters.

Dynalink already uses the Lookup objects for various purposes; it is used for 
linking invocations of @CallerSensitive-marked methods (which Dynalink 
supports) as well as for linkage-rules respecting creation of type conversions 
(it’s somewhat surprising that the concern would pop up there, but it does…) 
All just to say that Dynalink having to deal with JVM linkage rules isn’t 
foreign to it, it’s already doing it in some contexts, but it indeed currently 
doesn’t support linking to classes that aren’t world-exported.

Attila.

> 
> From what I could understand, in Nashorn, the generated classes
> were defined in the unnamed module, that always exports all the packages,
> so the access always worked.
> 
> The code can be found here:
> https://github.com/openjdk/jdk/blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/CheckRestrictedPackage.java#L94
> 
> Best regards.
> 
> Thiago



JDK Dynalink only works with unconditionally exported packages

2021-05-10 Thread Thiago Henrique Hupner
Hi all.

I've been testing the JDK Dynalink recently and
I think I've found a bug.

The class jdk.dynalink.beans.CheckRestrictedPackage checks
if a package is restricted.

So, I have a class that has some static methods. But to Dynalink find the
class,
the class needs to be public. OK,
I've changed my class to be public. However, it needs that
the package of the class is exported in the module too.

I think this is a little too restrictive. I don't want to expose
to my users some internal implementation.

The main problem is that the method
CheckRestrictedPackage::isRestrictedClass
does the following check:

// ...
final String pkgName = name.substring(0, i);
final Module module = clazz.getModule();
if (module != null && !module.isExported(pkgName)) {
return true;
 }
// ...

I have a feeling that this check should be changed to something like

// ...
final String pkgName = name.substring(0, i);
final Module module = clazz.getModule();
if (module != null && !module.isOpen(pkgName,
CheckRestrictedPackage.class.getModule())) {
return true;
 }
// ...

In this way, my code can only "opens" a single package to the jdk.dynalink
module,
not having to unconditionally export a package.

>From what I could understand, in Nashorn, the generated classes
were defined in the unnamed module, that always exports all the packages,
so the access always worked.

The code can be found here:
https://github.com/openjdk/jdk/blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/CheckRestrictedPackage.java#L94

Best regards.

Thiago