JEP draft: Prepare to Restrict The Use of JNI
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Tue Aug 29 23:23:27 UTC 2023
On 29/08/2023 23:07, CC wrote:
> I've looked through the JEP a bit more, and can confidently say that
> this will be the first step towards a future I, personally, would not
> want to live in regarding java.
>
> > Unfortunately, any interaction /at all/ between Java code and native
> code is risky because /it can compromise the integrity of applications
> and the Java Platform itself/. According to the policy of
> (https://openjdk.org/jeps/8305968), all JDK features that are capable
> of breaking integrity must obtain explicit approval from the end user
> or the application assembler.
>
> So what about reflection? Method handles? Accessing even just the
> smallest of internals? Unsafe? All of those could compromise the
> integrity of the java platform, and should therefore be locked behind
> the library user accepting them to be used. Why aren't they locked
> behind flags?
I believe this document (which has been shared several times):
https://openjdk.org/jeps/8305968
Covers all the bases you touch upon.
>
> > Calling native code can lead to arbitrary undefined behavior
> <https://en.wikipedia.org/wiki/Undefined_behavior>, including JVM
> crashes. Such problems cannot be prevented by the Java runtime or
> caught by Java code.
> Unsafe. Misconfigured method handles.
Unsafe is covered in the document above. I don't know what you mean by
"misconfigured method handles".
>
> > Native code and Java code often exchange data through byte buffers
> <https://docs.oracle.com/en/java/javase/20/docs/specs/jni/functions.html#newdirectbytebuffer>,
> which are regions of memory not managed by the JVM's garbage
> collector. Native code can produce a byte buffer that is backed by an
> invalid region of memory, and using the byte buffer in Java code is
> practically certain to cause undefined behavior.
> The foreign memory API will allow you to do that directly from the
> comfort of java.
This is incorrect. As documented in the FFM API JEP:
https://openjdk.org/jeps/442
certain functionalities of the FFM API are provided as "restricted
methods" which feature similar restrictions as the ones Ron is proposing
for JNI (disabled by default, enabled with flag). The JEP draft Ron
pointed at even contains a mapping between FFM restrictions and proposed
JNI restrictions, so it should be fairly evident that FFM is restricted
exactly in the same way proposed for JNI (and has been for the last 2
years).
>
> > Native code could use the JNI API to access fields
> <https://docs.oracle.com/en/java/javase/20/docs/specs/jni/functions.html#accessing-fields-of-objects>
> and call methods
> <https://docs.oracle.com/en/java/javase/20/docs/specs/jni/functions.html#calling-instance-methods>
> without any access checks by the JVM or even change the values of
> |final| fields long after they are initialized because the
> implementation of JNI does not perform the appropriate checks. Java
> code that uses JNI could therefore circumvent any of the invariants
> established through strong encapsulation of any other Java code.
> So what? There are legitimate reasons for this, changing internal
> fields may sometimes be required to make something work the way you
> want. There is no reason to use this as an argument against JNI,
> because it legitimately is a good use case. While I agree that
> changing a String instance's data array would be a bit silly, that
> example is also about the least practical example of this.
Here I completely disagree. While I can understand why you might think
that way - you are almost certainly underestimating the cost that is
associated with "changing internal fields [...] to make something work
the way you want".
I suggest you take a look at this:
https://bugs.openjdk.org/browse/JDK-8233873
And see how much difficult it is for the JIT to do something that looks
fairly trivial (constant folding of a final field) simply because it has
to be conservative of the many ways in which a final field can be
mutated (even if that's of course not allowed on paper).
And then there's forward-compatibility. If every Java
application/library out there had free access to every JDK internal,
that would mean that either (a) such applications/libraries would need
significant updates on each JDK release or (b) that the implementation
of the JDK stagnates to make sure that none of these
applications/libraries will break ever. Needless to say, both outcomes
are rather sad.
But we're digressing - while JNI *can* be used to escape from access
checks, I actually think that most developers resorting to JNI do that
because they need to access some functionality that is not provided by
the JDK. Note how this is also one of the driving use cases of the new
FFM API. That said, even though FFM can't mutate final fields (unlike
JNI), it is still important to acknowledge the fact that it's not just
an ordinary Java API. The functionalies it provides are powerful but, if
misused, can lead to very bad situations (JVM crashes, memory
corruption, you name it). And, as you say, since this can all be done
"from the comfort of java" there has to be some way for the JDK to say
that not all its API methods are created equal. We can quibble about the
specifics of the mechanism we pick to do this - but I hope we agree that
unleashing FFM API w/o any kind of opt-in for some of its unsafest
corners would *not* be a great thing for the wider Java ecosystem.
Maurizio
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jdk-dev/attachments/20230830/b5567da5/attachment-0001.htm>
More information about the jdk-dev
mailing list