From 8aff72ba655e0d45ff87dd3a270197c498ce3c1d Mon Sep 17 00:00:00 2001 From: Shilpi Rastogi Date: Thu, 19 May 2016 11:13:52 +0200 Subject: [PATCH] 8149574: Fix java/lang/invoke/MethodHandleImpl's use of Unsafe.defineAnonymousClass() Reviewed-by: vlivanov, psandoz, sundar, mhaupt --- .../java/lang/invoke/MethodHandleImpl.java | 131 +++++++++--------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index 6f2f35b8773..cc7e1e3fecc 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -41,9 +41,15 @@ import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; + +import jdk.internal.org.objectweb.asm.AnnotationVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; + import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; +import static jdk.internal.org.objectweb.asm.Opcodes.*; /** * Trusted implementation code for MethodHandle. @@ -1155,6 +1161,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // Put the whole mess into its own nested class. // That way we can lazily load the code and set up the constants. private static class BindCaller { + private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class); + static MethodHandle bindCaller(MethodHandle mh, Class hostClass) { // Do not use this function to inject calls into system classes. @@ -1173,38 +1181,15 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } private static MethodHandle makeInjectedInvoker(Class hostClass) { - Class bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null); - if (hostClass.getClassLoader() != bcc.getClassLoader()) - throw new InternalError(hostClass.getName()+" (CL)"); try { - if (hostClass.getProtectionDomain() != bcc.getProtectionDomain()) - throw new InternalError(hostClass.getName()+" (PD)"); - } catch (SecurityException ex) { - // Self-check was blocked by security manager. This is OK. - // In fact the whole try body could be turned into an assertion. - } - try { - MethodHandle init = IMPL_LOOKUP.findStatic(bcc, "init", MethodType.methodType(void.class)); - init.invokeExact(); // force initialization of the class - } catch (Throwable ex) { - throw uncaughtException(ex); - } - MethodHandle bccInvoker; - try { - MethodType invokerMT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class); - bccInvoker = IMPL_LOOKUP.findStatic(bcc, "invoke_V", invokerMT); + Class invokerClass = UNSAFE.defineAnonymousClass(hostClass, INJECTED_INVOKER_TEMPLATE, null); + assert checkInjectedInvoker(hostClass, invokerClass); + return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT); } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } - // Test the invoker, to ensure that it really injects into the right place. - try { - MethodHandle vamh = prepareForInvoker(MH_checkCallerClass); - Object ok = bccInvoker.invokeExact(vamh, new Object[]{hostClass, bcc}); - } catch (Throwable ex) { - throw new InternalError(ex); - } - return bccInvoker; } + private static ClassValue CV_makeInjectedInvoker = new ClassValue() { @Override protected MethodHandle computeValue(Class hostClass) { return makeInjectedInvoker(hostClass); @@ -1235,62 +1220,82 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return mh; } + private static boolean checkInjectedInvoker(Class hostClass, Class invokerClass) { + assert (hostClass.getClassLoader() == invokerClass.getClassLoader()) : hostClass.getName()+" (CL)"; + try { + assert (hostClass.getProtectionDomain() == invokerClass.getProtectionDomain()) : hostClass.getName()+" (PD)"; + } catch (SecurityException ex) { + // Self-check was blocked by security manager. This is OK. + } + try { + // Test the invoker to ensure that it really injects into the right place. + MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT); + MethodHandle vamh = prepareForInvoker(MH_checkCallerClass); + return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass }); + } catch (Throwable ex) { + throw new InternalError(ex); + } + } + private static final MethodHandle MH_checkCallerClass; static { final Class THIS_CLASS = BindCaller.class; - assert(checkCallerClass(THIS_CLASS, THIS_CLASS)); + assert(checkCallerClass(THIS_CLASS)); try { MH_checkCallerClass = IMPL_LOOKUP .findStatic(THIS_CLASS, "checkCallerClass", - MethodType.methodType(boolean.class, Class.class, Class.class)); - assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS, THIS_CLASS)); + MethodType.methodType(boolean.class, Class.class)); + assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS)); } catch (Throwable ex) { throw new InternalError(ex); } } @CallerSensitive - private static boolean checkCallerClass(Class expected, Class expected2) { - // This method is called via MH_checkCallerClass and so it's - // correct to ask for the immediate caller here. + private static boolean checkCallerClass(Class expected) { + // This method is called via MH_checkCallerClass and so it's correct to ask for the immediate caller here. Class actual = Reflection.getCallerClass(); - if (actual != expected && actual != expected2) - throw new InternalError("found "+actual.getName()+", expected "+expected.getName() - +(expected == expected2 ? "" : ", or else "+expected2.getName())); + if (actual != expected) + throw new InternalError("found " + actual.getName() + ", expected " + expected.getName()); return true; } - private static final byte[] T_BYTES; - static { - final Object[] values = {null}; - AccessController.doPrivileged(new PrivilegedAction<>() { - public Void run() { - try { - Class tClass = T.class; - String tName = tClass.getName(); - String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class"; - try (java.io.InputStream in = tClass.getResourceAsStream(tResource)) { - values[0] = in.readAllBytes(); - } - } catch (java.io.IOException ex) { - throw new InternalError(ex); - } - return null; - } - }); - T_BYTES = (byte[]) values[0]; - } + private static final byte[] INJECTED_INVOKER_TEMPLATE = generateInvokerTemplate(); - // The following class is used as a template for Unsafe.defineAnonymousClass: - private static class T { - static void init() { } // side effect: initializes this class - static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable { - return vamh.invokeExact(args); - } + /** Produces byte code for a class that is used as an injected invoker. */ + private static byte[] generateInvokerTemplate() { + ClassWriter cw = new ClassWriter(0); + + // private static class InjectedInvoker { + // @Hidden + // static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable { + // return vamh.invokeExact(args); + // } + // } + cw.visit(52, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null); + + MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V", + "(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;", + null, null); + + // Suppress invoker method in stack traces. + AnnotationVisitor av0 = mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); + av0.visitEnd(); + + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact", + "([Ljava/lang/Object;)Ljava/lang/Object;", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + + cw.visitEnd(); + return cw.toByteArray(); } } - /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */ private static final class WrappedMember extends DelegatingMethodHandle { private final MethodHandle target;