JDK-8233726 : A new Lookup::hasFullPrivilegeAccess method for testing full privilege access
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-11-06
  • Updated: 2019-12-04
  • Resolved: 2019-12-04
Related Reports
CSR :  
Description
Summary
-------

Add a new `Lookup::hasFullPrivilegeAccess` method to replace `Lookup::hasPrivateAccess`
and also update `Lookup::defineClass` behavior if this Lookup has full privilege access.

Problem
-------

This CSR is a follow-up due to the change by JDK-8226916

`Lookup::hasPrivateAccess` intends to test if this lookup is a full-power lookup; that is created by the original caller class calling `MethodHandles::lookup`.  The current specification for `Lookup::hasPrivateAccess` returns true if the lookup modes contain `PRIVATE` but it does not check `MODULE` bit.

In  addition, the `Lookup` class specification contains the [Discussion of private access](https://download.java.net/java/early_access/jdk14/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#privacc) section and [Security manager interactions](https://download.java.net/java/early_access/jdk14/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#secmgr) which needs to be re-examined.  Prior to Java SE 9, a Lookup with private access is equivalent to a Lookup with full-power access.   The list of capabilities need re-examination to determine what capabilities a Lookup with full-power access or with private access (no module access) are  allowed.   

Solution
--------

`MODULE` mode in Java SE 14 is used to represent a `Lookup` whose original creator is a member in the module of the lookup class.  `MODULE` bit will be dropped if teleporting from another module via `MethodHandles::privateLookupIn`.

A new correctly named method `Lookup::hasFullPrivilegeAccess` is introduced to test if the lookup modes contain `PRIVATE` and `MODULE` access.  Deprecate `Lookup::hasPrivateAccess` while `Lookup::hasPrivateAccess` is updated to call  `Lookup::hasFullPrivilegeAccess` to match the original intent of `Lookup::hasPrivateAccess`.

`MethodHandles::privateLookupIn` provides a mechanism for a framework
to do cross-module teleporting and perform deep reflection on private members of a package  opened to the caller module.  So Lookup with full-power access is needed only for the capability to create method handle for a caller-sensitive method. A Lookup with `PRIVATE` access possesses other  capabilities listed in the [Discussion of private access section](https://download.java.net/java/early_access/jdk14/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#privacc).

W.r.t. security manager interfaction, the security permission check is skipped for full power lookup.  `Lookup::defineClass` is updated to perform security permission check if the security manager is present and this lookup  <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>, consistent with other Lookup operations.

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

(1) `MethodHandles::privateLookupIn`

```
     /**
      * Returns a {@link Lookup lookup object} with
      * full capabilities to emulate all supported bytecode behaviors of the caller.
-     * These capabilities include <a href="MethodHandles.Lookup.html#privacc">private access</a> to the caller.
+     * These capabilities include {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access} to the caller.
      * Factory methods on the lookup object can create
      * <a href="MethodHandleInfo.html#directmh">direct method handles</a>
      * for any member that the caller has access to via bytecodes,
@@ -210,6 +210,7 @@
      * @since 9
      * @spec JPMS
      * @see Lookup#dropLookupMode
+     * @see Lookup#hasFullPrivilegeAccess()
      * @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a>
      */
     public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException {
```

(2) Lookup class specification

```
      * <a id="privacc"></a>
-     * <em>Discussion of private access:</em>
+     * <em>Discussion of private and module access:</em>
      * We say that a lookup has <em>private access</em>
      * if its {@linkplain #lookupModes lookup modes}
      * include the possibility of accessing {@code private} members
@@ -561,8 +560,6 @@
      * only lookups with private access possess the following capabilities:
      * <ul style="font-size:smaller;">
      * <li>access private fields, methods, and constructors of the lookup class and its nestmates
-     * <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods,
-     *     such as {@code Class.forName}
      * <li>create method handles which {@link Lookup#findSpecial emulate invokespecial} instructions
      * <li>avoid <a href="MethodHandles.Lookup.html#secmgr">package access checks</a>
      *     for classes accessible to the lookup class
@@ -570,6 +567,18 @@
      *     within the same package member
      * </ul>
      * <p style="font-size:smaller;">
+     * Similarly, a lookup with module access ensures that the original lookup creator was
+     * a member in the same module as the lookup class.
+     * <p style="font-size:smaller;">
+     * Private and module access are independently determined modes; a lookup may have
+     * either or both or neither.  A lookup which possesses both access modes is said to
+     * possess {@link #hasFullPrivilegeAccess() full privilege access}.  Such a lookup has
+     * the following additional capability:
+     * <ul style="font-size:smaller;">
+     * <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods,
+     *     such as {@code Class.forName}
+     * </ul>
+     * <p style="font-size:smaller;">
      * Each of these permissions is a consequence of the fact that a lookup object
      * with private access can be securely traced back to an originating class,
      * whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions
@@ -644,7 +653,7 @@
      * <p>
      * {@link MethodHandles#privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn(T.class, lookup)}
      * can be used to teleport a {@code lookup} from class {@code C} to class {@code T}
-     * and create a new {@code Lookup} with <a href="#privcc">private access</a>
+     * and create a new {@code Lookup} with <a href="#privacc">private access</a>
      * if the lookup class is allowed to do <em>deep reflection</em> on {@code T}.
      * The {@code lookup} must have {@link #MODULE} and {@link #PRIVATE} access
      * to call {@code privateLookupIn}.
@@ -1110,7 +1119,7 @@
      * the {@code refc} and {@code defc} values are the class itself.)
      * The value {@code lookc} is defined as <em>not present</em>
      * if the current lookup object does not have
-     * <a href="MethodHandles.Lookup.html#privacc">private access</a>.
+     * {@linkplain #hasFullPrivilegeAccess() full privilege access}.
      * The calls are made according to the following rules:
      * <ul>
      * <li><b>Step 1:</b>
@@ -1141,6 +1150,12 @@
      * Therefore, the above rules presuppose a member or class that is public,
      * or else that is being accessed from a lookup class that has
      * rights to access the member or class.
+     * <p>
+     * If a security manager is present and the current lookup object does not have
+     * {@linkplain #hasFullPrivilegeAccess() full privilege access}, then
+     * {@link #defineClass(byte[]) defineClass}
+     * calls {@link SecurityManager#checkPermission smgr.checkPermission}
+     * with {@code RuntimePermission("defineClass")}.
      *
      * <h2><a id="callsens"></a>Caller sensitive methods</h2>
      * A small number of Java methods have a special property called caller sensitivity.
@@ -1160,8 +1175,8 @@
      * <p>
      * In cases where the lookup object is
      * {@link MethodHandles#publicLookup() publicLookup()},
-     * or some other lookup object without
-     * <a href="MethodHandles.Lookup.html#privacc">private access</a>,
+     * or some other lookup object without the
+     * {@linkplain #hasFullPrivilegeAccess() full privilege access},
      * the lookup class is disregarded.
      * In such cases, no caller-sensitive method handle can be created,
      * access is forbidden, and the lookup fails with an
```

(3) `Lookup::defineClass`

```
      * access is forbidden, and the lookup fails with an
@@ -1514,7 +1525,8 @@
          * @apiNote
          * A lookup with {@code PACKAGE} but not {@code PRIVATE} mode can safely
          * delegate non-public access within the package of the lookup class without
-         * conferring private access.  A lookup with {@code MODULE} but not
+         * conferring  <a href="MethodHandles.Lookup.html#privacc"> private access</a>.
+         * A lookup with {@code MODULE} but not
          * {@code PACKAGE} mode can safely delegate {@code PUBLIC} access within
          * the module of the lookup class without conferring package access.
          * A lookup with a {@linkplain #previousLookupClass() previous lookup class}
@@ -1565,8 +1577,9 @@
          * run at a later time, as detailed in section 12.4 of the <em>The Java Language
          * Specification</em>. </p>
          *
-         * <p> If there is a security manager, its {@code checkPermission} method is first called
-         * to check {@code RuntimePermission("defineClass")}. </p>
+         * <p> If there is a security manager and this lookup does not have {@linkplain
+         * #hasPrivateAccess() full privilege access}, its {@code checkPermission} method
+         * is first called to check {@code RuntimePermission("defineClass")}. </p>
          *
          * @param bytes the class bytes
          * @return the {@code Class} object for the class
@@ -1575,7 +1588,8 @@
          * @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
          * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be
          * verified ({@code VerifyError}), is already defined, or another linkage error occurs
-         * @throws SecurityException if denied by the security manager
+         * @throws SecurityException if a security manager is present and it
+         *                           <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if {@code bytes} is {@code null}
          * @since 9
          * @spec JPMS
@@ -1584,12 +1598,13 @@
          * @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
```

(4) `Lookup::hasPrivateAccess`

```
         /**
-         * Returns {@code true} if this lookup has {@code PRIVATE} access.
-         * @return {@code true} if this lookup has {@code PRIVATE} access.
+         * Returns {@code true} if this lookup has {@code PRIVATE} and {@code MODULE} access.
+         * @return {@code true} if this lookup has {@code PRIVATE} and {@code MODULE} access.
+         * @deprecated This method was originally designed to test {@code PRIVATE} access
+         * that implies full privilege access but {@code MODULE} access has since become
+         * independent of {@code PRIVATE} access.  It is recommended to call
+         * {@link #hasFullPrivilegeAccess()} instead.
          * @since 9
          */
+        @Deprecated(since="14")
         public boolean hasPrivateAccess() {
```

(5) `Lookup::hasFullPrivilegeAccess`

```
+        /**
+         * Returns {@code true} if this lookup has <em>full privilege access</em>,
+         * i.e. {@code PRIVATE} and {@code MODULE} access.
+         * A {@code Lookup} object must have full privilege access in order to
+         * access all members that are allowed to the {@linkplain #lookupClass() lookup class}.
+         *
+         * @return {@code true} if this lookup has full privilege access.
+         * @since 14
+         * @see <a href="MethodHandles.Lookup.html#privacc">private and module access</a>
+         */
+        public boolean hasFullPrivilegeAccess()
```

(6) `Lookup::findClass` javadoc update is a spec clarification.  Not a spec change.

```
         /**
-         * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
-         * initializer of the class is not run.
-         * <p>
-         * The lookup context here is determined by the {@linkplain #lookupClass() lookup class}, its class
-         * loader, and the {@linkplain #lookupModes() lookup modes}. In particular, the method first attempts to
-         * load the requested class, and then determines whether the class is accessible to this lookup object.
+         * Looks up a class by name from the lookup context defined by this {@code Lookup} object,
+         * <a href="MethodHandles.Lookup.html#equiv">as if resolved</a> by an {@code ldc} instruction.
+         * Such a resolution, as specified in JVMS 5.4.3.1 section, attempts to locate and load the class,
+         * and then determines whether the class is accessible to this lookup object.
+         * <p>
+         * The lookup context here is determined by the {@linkplain #lookupClass() lookup class},
+         * its class loader, and the {@linkplain #lookupModes() lookup modes}.
          *
          * @param targetName the fully qualified name of the class to be looked up.
          * @return the requested class.
@@ -1948,9 +1968,8 @@
          * @throws ClassNotFoundException if the class cannot be loaded by the lookup class' loader.
          * @throws IllegalAccessException if the class is not accessible, using the allowed access
          * modes.
-         * @throws    SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @since 9
+         * @jvms 5.4.3.1 Class and Interface Resolution
          */
```

See attached MethodHandles.Lookup-report-v4.html and MethodHandles-report.html specdiff.
Comments
[~darcy] Thanks Joe. The typo was corrected in my local repo. The CSR in-line spec/diff is now fixed.
04-12-2019

Moving to Approved. The in-line spec text - * Returns {@code true} if this lookup has {@code PRIVATE} access. - * @return {@code true} if this lookup has {@code PRIVATE} access. + * Returns {@code true} if this lookup has {@code PRIVATE} and {@code MODULE} access. + * @return {@code true} if this lookup has {@code PRIVATE} and {@code MODULE} access. + * @deprecated This method was originally designed to test {@code PRIVATE} access + * that implies full privilege access but {@code PRIVATE} access has since become + * independent of {@code PRIVATE} access. It is recommended to call + * {@link #hasFullPrivilegeAccess()} instead. * @since 9 Looks to include a typo ("but {@code PRIVATE} access has since become independent of {@code PRIVATE} access") this is corrected in the spec diff; please double-check the right version gets pushed :-)
04-12-2019