From aaeb22eaaad27fa12325428a80069c7a6e352dd3 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 5 Aug 2021 20:16:48 +0200 Subject: [PATCH] [release] Avoid escaping Byte Buddy's scope when defining a class via a method handle to satisfy the security manager --- .../dynamic/loading/ClassInjector.java | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java index f79a07fcc72..b6f10c80907 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java @@ -45,6 +45,7 @@ import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.*; +import java.util.concurrent.ExecutionException; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; @@ -1528,6 +1529,11 @@ class UsingLookup extends AbstractBase { */ private static final MethodHandles.Lookup METHOD_HANDLES_LOOKUP = doPrivileged(JavaDispatcher.of(MethodHandles.Lookup.class)); + /** + * A reference to {@code java.lang.invoke.MethodHandles$Lookup#defineClass} or {@code null} if the method is not available. + */ + private static final Method DEFINE_CLASS = doPrivileged(DefineClassAction.INSTANCE); + /** * Indicates a lookup instance's package lookup mode. */ @@ -1618,8 +1624,10 @@ public Map> injectRaw(Map types) { throw new IllegalArgumentException(entry.getKey() + " must be defined in the same package as " + lookup); } try { - result.put(entry.getKey(), METHOD_HANDLES_LOOKUP.defineClass(lookup, entry.getValue())); - } catch (IllegalAccessException exception) { + result.put(entry.getKey(), (Class) DEFINE_CLASS.invoke(lookup, entry.getValue())); + } catch (InvocationTargetException exception) { + throw new IllegalStateException("Failed to define " + entry.getKey() + " using " + lookup, exception.getCause()); + } catch (Exception exception) { throw new IllegalStateException("Failed to define " + entry.getKey() + " using " + lookup, exception); } } @@ -1632,7 +1640,7 @@ public Map> injectRaw(Map types) { * @return {@code true} if the current VM is capable of defining classes using a lookup. */ public static boolean isAvailable() { - return ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtLeast(ClassFileVersion.JAVA_V9); + return DEFINE_CLASS != null; } /** @@ -1673,16 +1681,30 @@ interface Lookup { * @return The modifiers indicating the instance's lookup modes. */ int lookupModes(Object lookup); + } + } - /** - * Defines a class. - * - * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use. - * @param binaryRepresentation The defined class's binary representation. - * @return The defined class. - * @throws IllegalAccessException If the class definition is accessing an illegal package. - */ - Class defineClass(Object lookup, byte[] binaryRepresentation) throws IllegalAccessException; + /** + * A privileged action to resolve {@code java.lang.invoke.MethodHandles$Lookup#defineClass}, or {@code null}, + * if not available. This method must be invoked reflectively since the security manager checks the scope of + * invocation for this method such that no {@link JavaDispatcher} can be used. + */ + protected enum DefineClassAction implements PrivilegedAction { + + /** + * The singleton instance. + */ + INSTANCE; + + /** + * {@inheritDoc} + */ + public Method run() { + try { + return Class.forName("java.lang.invoke.MethodHandles$Lookup").getMethod("defineClass", byte[].class); + } catch (Exception ignored) { + return null; + } } } }