CSR :
|
|
Relates :
|
|
Relates :
|
Summary ------- Issue warnings about uses of the [Java Native Interface (JNI)](https://docs.oracle.com/en/java/javase/22/docs/specs/jni/index.html). These warnings aim to prepare developers for a future release that restricts the use of JNI by default in order to improve [integrity by default](https://openjdk.org/jeps/8305968). The warnings issued in this release, and the exceptions thrown in that future release, can be avoided by selectively enabling native access. Adjust the Foreign Function & Memory API ([JEP 454](https://openjdk.org/jeps/454)) to issue warnings in a consistent manner. Problem ------- Any interaction at all between Java code and native code is risky because it can compromise the integrity of applications and of the Java Platform itself. According to the policy of [integrity by default](https://openjdk.org/jeps/8305968), all JDK features that are capable of breaking integrity must obtain explicit approval from the application's developer. Solution -------- The FFM API [restricts](https://openjdk.org/jeps/454#Safety) the ability to load native libraries and to obtain method handles for native code; it does not restrict the ability to call native code. We propose here to do the same for JNI, by restricting the loading of native libraries and the binding of `native` methods; we will not restrict the calling of native methods. As with the FFM API, these restrictions can be lifted for specific modules by enabling native access for those modules. We will strengthen the effects of the restrictions over time, starting with warnings and proceeding gradually to unavoidable exceptions. Specification ------------- ### Enabling native access You can lift the native-access restrictions for selected modules, in effect acknowledging your application's need to load native libraries and bind `native` methods, in the same way as for the FFM API. At startup, use the command line option ``` $ java --enable-native-access=M,... ... ``` where `M,...` is a comma-separated list of modules that should be allowed to perform restricted operations. To lift restrictions on code on the class path, use ``` $ java --enable-native-access=ALL-UNNAMED ... ``` As an alternative to the `--enable-native-access` option, you can add this attribute to the manifest of an executable JAR file, i.e., a JAR file that is launched via the `java -jar` option: ``` Enable-Native-Access: ALL-UNNAMED ``` `ALL-UNNAMED` is the only supported value; other module names cause an exception to be thrown. When a module is created programmatically, you can enable native access for it via the [`ModuleLayer.Controller::enableNativeAccess`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/ModuleLayer.Controller.html#enableNativeAccess(java.lang.Module)) method, which is itself a restricted method. The [JNI Invocation API](https://docs.oracle.com/en/java/javase/22/docs/specs/jni/invocation.html) allows a native application to embed a JVM in its own process. A native application which uses the JNI Invocation API can enable native access for modules in the embedded JVM by passing the `--enable-native-access` option when [creating the JVM](https://docs.oracle.com/en/java/javase/22/docs/specs/jni/invocation.html#jni_createjavavm). Code can test, at runtime, whether or not its module has native access enabled with the method [Module.isNativeAccessEnabled](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Module.html#isNativeAccessEnabled()). ### Controlling the consequences of illegal native access Performing a restricted operation in a module that does not have native access is deemed illegal. What action the Java runtime takes when such an operation is attempted is controlled by a new command line option, `--illegal-native-access`, which is similar in spirit and form to the `--illegal-access` option introduced by [JEP 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation) in JDK 9. It works as follows: - `--illegal-native-access=allow` allows the operation to proceed. - `--illegal-native-access=warn` allows the operation but issues a warning the first time that illegal native access occurs in a particular module. At most one warning per module is issued. This mode is the default in JDK 24. It will be phased out in a future release and, eventually, removed. - `--illegal-native-access=deny` throws an `IllegalCallerException` exception for every illegal native access operation. This mode will become the default in a future release. When `deny` becomes the default mode then `allow` will be removed but `warn` will remain supported for at least one release. ### Aligning the FFM API In prior releases, if one or more modules were granted native access via the `--enable-native-access` option then attempts to call [restricted FFM methods](https://openjdk.org/jeps/454#Safety) from any other module would cause an `IllegalCallerException` to be thrown. To align the FFM API with JNI, we will relax this behavior so that illegal native access operations are treated exactly the same by the FFM API as in JNI. This means that, in JDK NN, such operations will result in warnings rather than exceptions. Applications currently using the FFM API can get the old behavior with this combination of options: ``` $ java --enable-native-access=M,... --illegal-native-access=deny ``` ### Warnings on loading native libraries Native libraries are loaded via the [`load`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Runtime.html#load(java.lang.String)) and [`loadLibrary`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Runtime.html#loadLibrary(java.lang.String)) methods of the `java.lang.Runtime` class. (The identically named convenience methods [`load`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/System.html#load(java.lang.String)) and [`loadLibrary`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/System.html#loadLibrary(java.lang.String)) of the `java.lang.System` class merely invoke the corresponding methods of the system-wide `Runtime` instance.) The methods `java.lang.Runtime.load`, `java.lang.System.load`, `java.lang.Runtime.loadLibrary`, and `java.lang.System.loadLibrary` are annotated with `@Restricted`. When a restricted method is called from a module that has not been granted native access via the `--enable-native-access` option, the JVM runs the method but, by default, issues a warning that identifies the caller: ``` WARNING: A restricted method in java.lang.System has been called WARNING: System::load has been called by com.foo.Server in an unnamed module (file:/path/to/comfoo.jar) WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module WARNING: Restricted methods will be blocked in a future release unless native access is enabled ``` The warning is written to the standard error stream. At most one such warning is issued for any particular module, and only if a native-access warning has not yet been issued for that module. (The _unnamed module_, mentioned in this example warning, is the module containing code on the class path.) ### Warnings on binding `native` methods When a `native` method in Java code is first called, the method is linked to the corresponding native code in a native library. This operation is called [_binding_](https://docs.oracle.com/javase/specs/jvms/se22/html/jvms-5.html#jvms-5.6) the `native` method. (The correspondence between the `native` method and the native code is described [here](https://docs.oracle.com/en/java/javase/22/docs/specs/jni/design.html#resolving-native-method-names).) Binding a native method is a restricted operation, just as obtaining a downcall handle via the FFM API’s [`Linker::downcallHandle`](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/Linker.html#downcallHandle(java.lang.foreign.MemorySegment,java.lang.foreign.FunctionDescriptor,java.lang.foreign.Linker.Option...)) method is restricted. When a `native` method is bound in a module that has not been granted native access via the `--enable-native-access` option, the JVM binds the method but, by default, issues a warning that identifies the caller: ``` WARNING: A restricted method in java.lang.System has been called WARNING: System::load has been called by com.foo.Server is declared in module foomod (file:/path/to/comfoo.jar) WARNING: Use --enable-native-access=foomod to avoid a warning for callers in this module WARNING: Restricted methods will be blocked in a future release unless native access is enabled ``` At most one such warning is issued for any particular module. Specifically: - The warning is issued only when a `native` method is bound, which happens the first time that the `native` method is called. The warning is not issued every time that the `native` method is called. - The warning is issued the first time that any `native` method declared in a particular module is bound, unless a native-access warning has already been issued for that module. ## Identifying the use of native code - The JFR events `jdk.NativeLibraryLoad` and `jdk.NativeLibraryUnload` track the loading and unloading of native libraries. - To help identify libraries that use JNI, a new JDK tool, `jnativescan`, statically scans code in a provided module/class path and reports use of restricted methods as well as classes declaring `native` methods. The tool is described in the CSR JDK-8334569. ## Documentation - Javadoc diff: https://cr.openjdk.org/~mcimadamore/jdk/8331671/v1/specdiff_out/ - Man page: https://github.com/openjdk/jdk-sandbox/blob/58fca7e89f52deb47c6d320f0193f996b6024b47/src/java.base/share/man/java.1#L551 ## Man page diff ``` \f[V]--enable-native-access\f[R] \f[I]module\f[R][\f[V],\f[R]\f[I]module\f[R]...] Native access involves access to code or data outside the Java runtime. This is generally unsafe and, if done incorrectly, might crash the JVM or result in memory corruption. Native access can occur as a result of calling a method that is either \f[B]restricted\f[R] [https://openjdk.org/jeps/454#Safety], or \f[V]native\f[R]. This option allows code in the specified modules to perform native access. Native access occurring in a module that has not been explicitly enabled is deemed \f[I]illegal\f[R]. .RS .PP \f[I]module\f[R] can be a module name, or \f[V]ALL-UNNAMED\f[R] to indicate code on the class path. .RE .TP -\f[V]--illegal-native-access=\f[R]\f[I]parameter\f[R] This option specifies a mode for how illegal native access is handled: .RS .RS .PP \f[B]Note:\f[R] This option will be removed in a future release. .RE .IP \[bu] 2 \f[V]allow\f[R]: This mode allows illegal native access in all modules, without any warings. .IP \[bu] 2 \f[V]warn\f[R]: This mode is identical to \f[V]allow\f[R] except that a warning message is issued for the first illegal native access found in a module. This mode is the default for the current JDK but will change in a future release. .IP \[bu] 2 \f[V]deny\f[R]: This mode disables all illegal native access except for those modules enabled by the \f[V]--enable-native-access\f[R] command-line option. That is, any illegal native access causes an \f[V]IllegalCallerException\f[R]. This mode will become the default in a future release. .PP To verify that your application is ready for a future version of the JDK, run it with \f[V]--illegal-native-access=deny\f[R] along with any necessary \f[V]--enable-native-access\f[R] options. ```
|