1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @library /test/lib
  27  * @modules java.base/jdk.internal.org.objectweb.asm
  28  * @build  HiddenNestmateTest
  29  * @run testng/othervm HiddenNestmateTest
  30  */
  31 
  32 import java.lang.invoke.*;
  33 import java.lang.invoke.MethodHandles.Lookup;
  34 import java.lang.reflect.InvocationTargetException;
  35 import java.lang.reflect.Method;
  36 import java.util.stream.Stream;
  37 import java.util.Arrays;
  38 
  39 import jdk.internal.org.objectweb.asm.*;
  40 import org.testng.annotations.Test;
  41 
  42 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
  43 import static java.lang.invoke.MethodHandles.Lookup.*;
  44 
  45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  46 import static org.testng.Assert.*;
  47 
  48 public class HiddenNestmateTest {
  49     private static final byte[] bytes = classBytes("HiddenInjected");
  50 
  51     private static void assertNestmate(Lookup lookup) {
  52         assertTrue((lookup.lookupModes() & PRIVATE) != 0);
  53         assertTrue((lookup.lookupModes() & MODULE) != 0);
  54 
  55         Class<?> hiddenClass = lookup.lookupClass();
  56         Class<?> nestHost = hiddenClass.getNestHost();
  57         assertTrue(hiddenClass.isHidden());
  58         assertTrue(nestHost == MethodHandles.lookup().lookupClass());
  59 
  60         // hidden nestmate is not listed in the return array of getNestMembers
  61         assertTrue(Stream.of(nestHost.getNestMembers()).noneMatch(k -> k == hiddenClass));
  62         assertTrue(hiddenClass.isNestmateOf(lookup.lookupClass()));
  63         assertTrue(Arrays.equals(hiddenClass.getNestMembers(), nestHost.getNestMembers()));
  64     }
  65 
  66     /*
  67      * Test a hidden class to have no access to private members of another class
  68      */
  69     @Test
  70     public void hiddenClass() throws Throwable {
  71         // define a hidden class
  72         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
  73         Class<?> c = lookup.lookupClass();
  74         assertTrue(lookup.hasFullPrivilegeAccess());
  75         assertTrue(c.getNestHost() == c);  // host of its own nest
  76         assertTrue(c.isHidden());
  77 
  78         // invoke int test(HiddenNestmateTest o) via MethodHandle
  79         MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
  80         MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
  81         try {
  82             int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this);
  83             throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
  84         } catch (IllegalAccessError e) {}
  85 
  86         // invoke int test(HiddenNestmateTest o)
  87         try {
  88             int x1 = testInjectedClass(c);
  89             throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
  90         } catch (IllegalAccessError e) {}
  91     }
  92 
  93     /*
  94      * Test a hidden class to have access to private members of its nestmates
  95      */
  96     @Test
  97     public void hiddenNestmate() throws Throwable {
  98         // define a hidden nestmate class
  99         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
 100         Class<?> c = lookup.lookupClass();
 101         assertNestmate(lookup);
 102 
 103         // invoke int test(HiddenNestmateTest o) via MethodHandle
 104         MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
 105         MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
 106         int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this);
 107         assertTrue(x == privMethod());
 108 
 109         // invoke int test(HiddenNestmateTest o)
 110         int x1 = testInjectedClass(c);
 111         assertTrue(x1 == privMethod());
 112     }
 113 
 114     /*
 115      * Test a hidden class created with NESTMATE and STRONG option is a nestmate
 116      */
 117     @Test
 118     public void hiddenStrongClass() throws Throwable {
 119         // define a hidden class strongly referenced by the class loader
 120         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, STRONG);
 121         assertNestmate(lookup);
 122     }
 123 
 124     /*
 125      * Fail to create a hidden class if dropping PRIVATE lookup mode
 126      */
 127     @Test(expectedExceptions = IllegalAccessException.class)
 128     public void noPrivateLookupAccess() throws Throwable {
 129         Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
 130         lookup.defineHiddenClass(bytes, false, NESTMATE);
 131     }
 132 
 133     public void teleportToNestmate() throws Throwable {
 134         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
 135         assertNestmate(lookup);
 136 
 137         // Teleport to a hidden nestmate
 138         Lookup lc =  MethodHandles.lookup().in(lookup.lookupClass());
 139         assertTrue((lc.lookupModes() & PRIVATE) != 0);
 140         Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
 141         assertNestmate(lc2);
 142     }
 143 
 144     /*
 145      * Fail to create a hidden class in a different package from the lookup class' package
 146      */
 147     @Test(expectedExceptions = IllegalArgumentException.class)
 148     public void notSamePackage() throws Throwable {
 149         MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE);
 150     }
 151 
 152     /*
 153      * invoke int test(HiddenNestmateTest o) method defined in the injected class
 154      */
 155     private int testInjectedClass(Class<?> c) throws Throwable {
 156         try {
 157             Method m = c.getMethod("test", HiddenNestmateTest.class);
 158             return (int) m.invoke(c.newInstance(), this);
 159         } catch (InvocationTargetException e) {
 160             throw e.getCause();
 161         }
 162     }
 163 
 164     private static byte[] classBytes(String classname) {
 165         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 166         MethodVisitor mv;
 167 
 168         cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null);
 169 
 170         {
 171             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 172             mv.visitCode();
 173             mv.visitVarInsn(ALOAD, 0);
 174             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
 175             mv.visitInsn(RETURN);
 176             mv.visitMaxs(0, 0);
 177             mv.visitEnd();
 178         }
 179         {
 180             // access a private member of the nest host class
 181             mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null);
 182             mv.visitCode();
 183             mv.visitVarInsn(ALOAD, 0);
 184             mv.visitVarInsn(ALOAD, 1);
 185             mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I");
 186             mv.visitInsn(IRETURN);
 187             mv.visitMaxs(0, 0);
 188             mv.visitEnd();
 189         }
 190         cw.visitEnd();
 191 
 192         return cw.toByteArray();
 193     }
 194 
 195     private int privMethod() { return 1234; }
 196 }