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

Lookup returned by MethodHandles.privateLookupIn is less powerful than before

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P3
    • Resolution: Not an Issue
    • Affects Version/s: 14
    • Fix Version/s: None
    • Component/s: core-libs

      Description

      ADDITIONAL SYSTEM INFORMATION :
      openjdk version "14-ea" 2020-03-17
      OpenJDK Runtime Environment (build 14-ea+10-332)
      OpenJDK 64-Bit Server VM (build 14-ea+10-332, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      MethodHandles.privateLookupIn now records the original lookup class and checks readability against both original lookup class and the target class.

      This change in behavior means that code that was valid and permitted once no longer runs.
      The behavior matches the new specification of privateLookupIn, but this new specification causes a regression.

      REGRESSION : Last worked in version 12.0.2

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Define 3 modules, named "a", "b" and "c".
      The module "a" should be open.
      Export a package in "b" only to module "a".
      Use MethodHandles.privateLookupIn() with a Lookup from a class in "c" on a class in module "a".
      Try to access a class in module "b" with the resulting lookup.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Code works as it did in Java 9, 10, 11, 12.

      data=test
      ACTUAL -
      An exception is thrown:

      Exception in thread "main" java.lang.IllegalAccessException: access to public member failed: b.B.data/java.lang.String/getField, from class a.A (module a), previous lookup c.Serializer (module c)
              at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:950)
              at java.base/java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:2933)
              at java.base/java.lang.invoke.MethodHandles$Lookup.checkField(MethodHandles.java:2883)
              at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectFieldCommon(MethodHandles.java:3092)
              at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectFieldNoSecurityManager(MethodHandles.java:3087)
              at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectField(MethodHandles.java:2610)
              at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectGetter(MethodHandles.java:2566)
              at c/c.Serializer.serialize(Serializer.java:23)
              at a/a.A.main(A.java:10)

      ---------- BEGIN SOURCE ----------
      --- a/module-info.java
      open module a {
      requires b;
      requires c;
      }
      --- a/a/A.java
      package a;

      import b.B;
      import c.Serializer;

      public class A {
      public static void main(String[] args) throws Throwable {
      B b = new B("test");
      System.out.println(Serializer.serialize(b));
      }
      }
      --- b/module-info.java
      module b {
      exports b to a;
      }
      --- b/b/B.java
      package b;

      public class B {
      public final String data;

      public B(String data) {
      this.data = data;
      }
      }
      --- c/module-info.java
      module c {
      exports c;
      }
      --- c/c/Serializer.java
      package c;

      import java.lang.invoke.MethodHandles;
      import java.lang.invoke.MethodHandles.Lookup;
      import java.lang.reflect.Field;

      public class Serializer {
      private static final StackWalker WALKER = StackWalker
      .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
      private static final Lookup LOOKUP = MethodHandles.lookup();

      public static String serialize(Object o) throws Throwable {
      Class<?> caller = WALKER.getCallerClass();
      // Add a read edge to the caller module
      Serializer.class.getModule().addReads(caller.getModule());
      Lookup remote = MethodHandles.privateLookupIn(caller, LOOKUP);

      StringBuilder sb = new StringBuilder();
      String sep = "";
      for (Field f : o.getClass().getDeclaredFields()) {
      sb.append(f.getName());
      sb.append("=");
      sb.append(remote.unreflectGetter(f).invoke(o));
      sb.append(sep);
      sep = ",";
      }
      return sb.toString();
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Call MethodHandles.lookup() using the result of privateLookupIn and use that lookup instead.

      Lookup remote = (Lookup) MethodHandles.privateLookupIn(caller, LOOKUP).findStatic(MethodHandles.class, "lookup", methodType(Lookup.class)).invokeExact();

      FREQUENCY : always


        Attachments

          Activity

            People

            Assignee:
            fmatte Fairoz Matte
            Reporter:
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: