From thihup at gmail.com Mon May 10 23:51:52 2021 From: thihup at gmail.com (Thiago Henrique Hupner) Date: Mon, 10 May 2021 20:51:52 -0300 Subject: JDK Dynalink only works with unconditionally exported packages Message-ID: 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 From szegedia at gmail.com Tue May 11 13:29:14 2021 From: szegedia at gmail.com (Attila Szegedi) Date: Tue, 11 May 2021 15:29:14 +0200 Subject: JDK Dynalink only works with unconditionally exported packages In-Reply-To: References: Message-ID: <50952190-5013-491D-B295-5A28E7833B61@gmail.com> 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