Review Request: 8221530: Caller sensitive methods not handling caller = null when invoked by JNI code with no java frames on stack

Peter Levart peter.levart at gmail.com
Thu Mar 28 14:48:04 UTC 2019


Hi,

On 3/28/19 9:40 AM, Alan Bateman wrote:
> On 27/03/2019 23:17, Mandy Chung wrote:
>> :
>>
>> The proposed fix is to perform proper access check.  When there is no
>> caller frame, it only allows to access to public members of a public 
>> type
>> in an unconditional exported API package.
>>
> The approach seems reasonable to me and we should, at some point, try 
> to align the other @CS methods with this behavior. As Mandy knows, 
> this is unspecified behavior and we aren't consistent in the JDK on 
> how to handle "no caller frame" case. Some @CS methods will throw NPE 
> if invoked directly, others detect the caller is null and throw or 
> treat it as Object.class.
>
> In the patch, shouldn't slowVerifyAccess check that memberClass is 
> public?

I think it should. If we pretend that the check should be equivalent as 
if performed from an unrelated class in an unrelated module, then the 
check for public member class should be included.

In addition, if access from null caller is granted and it is performed 
to a member in a "concealed" package, there's no warning displayed (the 
further logic in the AccessibleObject is skipped).

What would it look like if AccessibleObject was left intact and only 
Reflection was modified to accommodate for null currentClass - 
caller/accessor. Like the in the following patch:

Index: src/java.base/share/classes/jdk/internal/reflect/Reflection.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/java.base/share/classes/jdk/internal/reflect/Reflection.java 
(revision 54324:9d5c84b0a59862859081e2477f8069a2149c4063)
+++ src/java.base/share/classes/jdk/internal/reflect/Reflection.java 
(revision 54324+:9d5c84b0a598+)
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights 
reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights 
reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
@@ -108,7 +108,10 @@
      /**
       * Verify access to a member and return {@code true} if it is granted.
       *
-     * @param currentClass the class performing the access
+     * @param currentClass the class performing the access or null if
+     *                     access is to be verified for an unknown 
caller/accessor
+     *                     for example if access is performed via JNI 
and there's no
+     *                     Java frame on caller stack (custom JVM 
launchers)
       * @param memberClass the declaring class of the member being accessed
       * @param targetClass the class of target object if accessing instance
       *                    field or method;
@@ -122,6 +125,13 @@
                                               Class<?> targetClass,
                                               int modifiers)
      {
+        Objects.requireNonNull(memberClass);
+
+        if (currentClass == null) {
+            // verify access for an unknown caller/accessor
+            return verifyGeneralMemberAccess(memberClass, modifiers);
+        }
+
          if (currentClass == memberClass) {
              // Always succeeds
              return true;
@@ -201,6 +211,17 @@
          return true;
      }

+    /**
+     * @param memberClass the member's declaring class
+     * @param modifiers the member's modifiers
+     * @return {@code true} if the member can be accessed from anywhere
+     */
+    private static boolean verifyGeneralMemberAccess(Class<?> 
memberClass, int modifiers) {
+        return Modifier.isPublic(getClassAccessFlags(memberClass)) &&
+               Modifier.isPublic(modifiers) &&
+ memberClass.getModule().isExported(memberClass.getPackageName());
+    }
+
      /**
       * Returns {@code true} if memberClass's module exports memberClass's
       * package to currentModule.
@@ -327,6 +348,9 @@
int modifiers)
          throws IllegalAccessException
      {
+        if (currentClass == null)
+            return newIllegalAccessException(memberClass, modifiers);
+
          String currentSuffix = "";
          String memberSuffix = "";
          Module m1 = currentClass.getModule();
@@ -352,6 +376,37 @@
              if (m2.isNamed()) msg += " to " + m1;
          }

+        return new IllegalAccessException(msg);
+    }
+
+    /**
+     * Returns an IllegalAccessException with an exception message where
+     * there is no caller frame.
+     */
+    public static IllegalAccessException 
newIllegalAccessException(Class<?> memberClass,
+ int modifiers)
+        throws IllegalAccessException
+    {
+        String memberSuffix = "";
+        Module m2 = memberClass.getModule();
+        if (m2.isNamed())
+            memberSuffix = " (in " + m2 + ")";
+
+        String memberPackageName = memberClass.getPackageName();
+
+        String msg = "JNI attached native thread (null caller frame) 
cannot access ";
+        if (m2.isExported(memberPackageName)) {
+
+            // module access okay so include the modifiers in the message
+            msg += "a member of " + memberClass + memberSuffix +
+                " with modifiers \"" + Modifier.toString(modifiers) + "\"";
+
+        } else {
+            // module access failed
+            msg += memberClass + memberSuffix+ " because "
+                + m2 + " does not export " + memberPackageName;
+        }
+
          return new IllegalAccessException(msg);
      }



Regards, Peter



More information about the core-libs-dev mailing list