JDK-8280831 : AccessibleObject::setAccessible throws NPE when invoked by JNI code with no java frame on stack
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 19
  • Submitted: 2022-01-27
  • Updated: 2022-01-31
  • Resolved: 2022-01-31
Related Reports
CSR :  
Description
Summary
-------

Clarify the behavior of `java.lang.reflect.AccessibleObject::setAccessible` and  `trySetAccessible` APIs when invoked by JNI code with no Java frames on the stack that it only allows access to public member of a public type that is unconditionally exported per the access check as described in the class specification.

Problem
-------

The current implementation of `AccessibleObject::setAccessible` and  `trySetAccessible` throws NPE when invoked by  JNI code with no Java class on the stack.  The behavior of `AccessibleObject::setAccessible` and  `trySetAccessible` should be consistent with the access check.

The class spec of `AccessibleObject` has been updated in Java 13 (JDK-8221618) that JNI code with no Java class on the stack can only access public members of a public type that is in a package that is exported unconditionally.   

Solution
--------

Fix the implementation of `setAccessible` and `trySetAccessible` to allow setting the accessible flag on public members of a public type that is exported conditionally when invoked by a native thread attaches to the VM with no caller frame.    Also clarify the specification of `canAccess`,  `setAccessible` and `trySetAccessible` w.r.t. when invoked by JNI code with no Java frame on the stack.

Specification
-------------

`AccessibleObject::setAccessible(boolean)`

```
      *     open module. </li>
      * </ul>
      *
+     * <p> This method may be used by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
+     * with no caller class on the stack to enable access to a {@link Member member}
+     * of {@link Member#getDeclaringClass() declaring class} {@code D} if and only if:
+     * <ul>
+     *     <li> The member is {@code public} and {@code D} is {@code public} in
+     *     a package that the module containing {@code D} {@link
+     *     Module#isExported(String,Module) exports} unconditionally. </li>
+     * </ul>
+     *
      * <p> This method cannot be used to enable access to private members,
      * members with default (package) access, protected instance members, or
      * protected constructors when the declaring class is in a different module
```

`AccessibleObject::trySetAccessible`
```
+     * <p> If this method is invoked by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
+     * with no caller class on the stack, the {@code accessible} flag can
+     * only be set if the member and the declaring class are public, and
+     * the class is in a package that is exported unconditionally. </p>
+     *
```   

`AccessibleObject::canAccess`

```
-     * with the variation noted in the class description. </p>
+     * with the variation noted in the class description.
+     * If this method is invoked by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
+     * with no caller class on the stack, this method returns {@code true}
+     * if the member and the declaring class are public, and the class is in
+     * a package that is exported unconditionally. </p>

```

Comments
Moving to Approved; please consider a release note.
31-01-2022

Just to be clear, as stated in the PR, there is no spec change here as the class docs already indicated that this should be the behaviour. The repeating of that detail in each method is just a clarification. The main thing to evaluate here is the change in implementation behaviour. It is possible that application code has encountered these NPE's and so added logic to handle this native case. That logic will now need to change. But I agree with Alan that it seems unlikely that these methods would be be called directly from JNI with no other Java frames on the stack.
31-01-2022

In general I wouldn't expect it is common for JNI code to invoke these methods directly. The compatibility risk of the change proposed here is minimal because it's a NPE today to invoke these methods without any Java frames on the stack.
28-01-2022