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