Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8221618

Reflection member access not handling caller = null when invoked by JNI code with no java frames on stack

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 13
    • core-libs
    • None
    • behavioral
    • low
    • Hide
      If a native thread attaches to the VM and call `Field::get`, `Field::set`,
      `Method::invoke`, `Constructor::newInstance` by JNI code with no Java
      frame on stack, with this change, `IllegalAccessException` will be thrown
      if it attempts to access a non-public member or a public member in a
      non-exported type. This is an incompatible behavioral change as JDK 11
      and earlier releases may succeed if it was called very early in the application
      execution.

      There is no change to the behavior of JNI GetFieldID and GetObjectField.
      Show
      If a native thread attaches to the VM and call `Field::get`, `Field::set`, `Method::invoke`, `Constructor::newInstance` by JNI code with no Java frame on stack, with this change, `IllegalAccessException` will be thrown if it attempts to access a non-public member or a public member in a non-exported type. This is an incompatible behavioral change as JDK 11 and earlier releases may succeed if it was called very early in the application execution. There is no change to the behavior of JNI GetFieldID and GetObjectField.
    • Java API
    • SE

      Summary

      Method::invoke, Field::get, Field::set, Constructor::newInstance APIs are specified to perform the access check against the caller class when the API is invoked. This CSR proposes to change the behavior if the API is invoked by JNI code with no java frames on stack and allows access to public member of a public type that is unconditionally exported.

      Problem

      Method.invoke and Field.get and other core reflection API to access a member reflectively assumes there is always a caller class on the stack. It is not specified when it is invoked by JNI code with no java frames on stack. The long-standing behavior seems to be accidental that the implementation of a AccessibleObject has a cache that keeps the most recent access-checked caller class. When Field::get is invoked early and by JNI code with null caller, the cache is null and the access succeeded in JDK 11 as cache and caller both are null. It would break encapsulation. JDK-8206240 introduced a regression that does not handle null caller and resulting in NPE.

      Solution

      The proposed solution is to perform proper access check. When Method::invoke, Field::get, Field::set, Constructor::newInstance are invoked by JNI code with no java frame on stack, it allows to access to public members of a public type in an unconditional exported API package.

      If a native thread attaches to the VM and attempts to access a non-public member or a public member in a non-exported type e.g. jdk.internal.misc.Unsafe, it will throw IllegalAccessException.

      Specification

      Update the java.lang.reflect.AccessibleObject class specification as follows:

      --- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
      +++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
      @@ -56,7 +56,10 @@
        * {@code Field}s, {@code Method}s, or {@code Constructor}s are used to get or
        * set fields, to invoke methods, or to create and initialize new instances of
        * classes, respectively. Every reflected object checks that the code using it
      - * is in an appropriate class, package, or module. </p>
      + * is in an appropriate class, package, or module. The check when invoked by
      + * <a href="{@docRoot}/../specs/jni/index.html">JNI code</a> with no Java
      + * class on the stack only succeeds if the member and the declaring class are
      + * public, and the class is in a package that is exported to all modules. </p>

            mchung Mandy Chung (Inactive)
            shadowbug Shadow Bug
            Alan Bateman
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: