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((lookup.lookupModes() & ORIGINAL) == ORIGINAL); 76 assertTrue(c.getNestHost() == c); // host of its own nest 77 assertTrue(c.isHidden()); 78 79 // invoke int test(HiddenNestmateTest o) via MethodHandle 80 MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class)); 81 MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class)); 82 try { 83 int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this); 84 throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()"); 85 } catch (IllegalAccessError e) {} 86 87 // invoke int test(HiddenNestmateTest o) 88 try { 89 int x1 = testInjectedClass(c); 90 throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()"); 91 } catch (IllegalAccessError e) {} 92 } 93 94 /* 95 * Test a hidden class to have access to private members of its nestmates 96 */ 97 @Test 98 public void hiddenNestmate() throws Throwable { 99 // define a hidden nestmate class 100 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE); 101 Class<?> c = lookup.lookupClass(); 102 assertNestmate(lookup); 103 104 // invoke int test(HiddenNestmateTest o) via MethodHandle 105 MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class)); 106 MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class)); 107 int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this); 108 assertTrue(x == privMethod()); 109 110 // invoke int test(HiddenNestmateTest o) 111 int x1 = testInjectedClass(c); 112 assertTrue(x1 == privMethod()); 113 } 114 115 /* 116 * Test a hidden class created with NESTMATE and STRONG option is a nestmate 117 */ 118 @Test 119 public void hiddenStrongClass() throws Throwable { 120 // define a hidden class strongly referenced by the class loader 121 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, STRONG); 122 assertNestmate(lookup); 123 } 124 125 /* 126 * Fail to create a hidden class if dropping PRIVATE lookup mode 127 */ 128 @Test(expectedExceptions = IllegalAccessException.class) 129 public void noPrivateLookupAccess() throws Throwable { 130 Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); 131 lookup.defineHiddenClass(bytes, false, NESTMATE); 132 } 133 134 public void teleportToNestmate() throws Throwable { 135 Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE); 136 assertNestmate(lookup); 137 138 // Teleport to a hidden nestmate 139 Lookup lc = MethodHandles.lookup().in(lookup.lookupClass()); 140 assertTrue((lc.lookupModes() & PRIVATE) != 0); 141 assertTrue((lc.lookupModes() & ORIGINAL) == 0); 142 143 Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE); 144 assertNestmate(lc2); 145 } 146 147 /* 148 * Fail to create a hidden class in a different package from the lookup class' package 149 */ 150 @Test(expectedExceptions = IllegalArgumentException.class) 151 public void notSamePackage() throws Throwable { 152 MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE); 153 } 154 155 /* 156 * invoke int test(HiddenNestmateTest o) method defined in the injected class 157 */ 158 private int testInjectedClass(Class<?> c) throws Throwable { 159 try { 160 Method m = c.getMethod("test", HiddenNestmateTest.class); 161 return (int) m.invoke(c.newInstance(), this); 162 } catch (InvocationTargetException e) { 163 throw e.getCause(); 164 } 165 } 166 167 private static byte[] classBytes(String classname) { 168 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); 169 MethodVisitor mv; 170 171 cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null); 172 173 { 174 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 175 mv.visitCode(); 176 mv.visitVarInsn(ALOAD, 0); 177 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); 178 mv.visitInsn(RETURN); 179 mv.visitMaxs(0, 0); 180 mv.visitEnd(); 181 } 182 { 183 // access a private member of the nest host class 184 mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null); 185 mv.visitCode(); 186 mv.visitVarInsn(ALOAD, 0); 187 mv.visitVarInsn(ALOAD, 1); 188 mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I"); 189 mv.visitInsn(IRETURN); 190 mv.visitMaxs(0, 0); 191 mv.visitEnd(); 192 } 193 cw.visitEnd(); 194 195 return cw.toByteArray(); 196 } 197 198 private int privMethod() { return 1234; } 199 }