On 16/11/2023 18:15, Maurizio Cimadamore wrote:
On 16/11/2023 16:54, Rob Spoor wrote:
Hi Maurizio,
I don't think you understand what my module is doing. For instance,
it's not specifying the downcall method handles themselves, it's just
making it easy to define them. Maybe a small example would show what
I'm doing.
Consider the following partial interface from JNA:
public interface Kernel32 extends StdCallLibrary, WinNT, Wincon {
/** The instance. */
Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class,
W32APIOptions.DEFAULT_OPTIONS);
int GetLastError();
}
What JNA is doing is creating an implementation based on this
interface that delegates to the native kernel32 library. This is all
done through the Native.load method.
What I'm building is not an FFM-backed version of Kernel32 interface,
but something like Native.load. In other words, my module will call
the restricted Linker.downcallHandle method. Somebody that then wants
to build a Kernel32 interface would not directly call any restricted
methods. As a result, their code would not need to have native access
enabled because they would get it through my module. That's something
I want to protect against, because it's definitely not safe to just
grant any access. (In fact, some incorrectly written test code has
already crashed my JVM.) While my module will definitely need to have
native access enabled, I'd like to require that the module with such a
Kernel32 interface needs to have native access enabled as well.
I believe I understood what you were attempting to do :-)
And I still stand by what I said yesterday - a client will need to trust
your library (and a flag is required there) to generate the correct
stuff when it gives you an interface (which your framework will generate
an implementation of using some black magic). Whether the client itself
is enabled to do native access or not is irrelevant, because unsafe
operation will not occur on the client module, they will occur on the
module of your framework. The crucial part here is that for an
application to be able to use your framework _some_ form of
`--enable-native-access` will be required (so, the command line will
give away the fact that the application will require some form of unsafe
access somewhere).
I understand that my module needs to be included in
`--enable-native-access` (or programmatically), I'm just trying to force
direct callers of my own restricted methods to be included as well.
Your answer below makes it clear this isn't something that can (easily)
be made public. That means I'll have to perform my own security checks.
Using a stack walk or MethodHandle.Lookup to find the caller class
provides me with an alternative to Reflection.getCallerClass(), which
indeed cannot be called directly by any non-JDK code.
Module.isNativeAccessEnabled() allows me to check if a module is
included in the JDK's --enable-native-access flag (or its alternatives),
but I can't use the JDK's handling if a module doesn't have access.
As for documentation, I'll have to find another way - either a custom
@Documented annotation, or simply Javadoc text.
Thanks for the time spent on this discussion :)
Rob
The idea of using a MethodHandles.Lookup instance looks interesting,
but it would only replace the use of the StackWalker. I still would
need to print a message or throw an exception from my module instead
of reusing the code from Module.ensureNativeAccess, which makes my
module either stricter than the FFM API right now, or less strict in
the future when the FFM API starts throwing exceptions.
What you are asking boils down to: how can I define _custom_ restricted
methods?
The answer is you can't. Restricted methods are a feature of the Java SE
API (pretty much like preview methods). And I believe this is likely to
remain that way: as you noticed, emulating a restricted method from
custom code requires a stackwalk, which we avoid using caller sensitive
methods (which are also not available outside the JDK).
Maurizio