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

Lookup returned by MethodHandles.privateLookupIn is less powerful than before

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P3 P3
    • None
    • 14
    • core-libs

      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


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

              Created:
              Updated:
              Resolved: