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.isHiddenClass()); 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.isHiddenClass()); 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 }