Skip to content

Commit

Permalink
[release] Avoid escaping Byte Buddy's scope when defining a class via…
Browse files Browse the repository at this point in the history
… a method handle to satisfy the security manager
  • Loading branch information
raphw committed Aug 5, 2021
1 parent a16b532 commit aaeb22e
Showing 1 changed file with 34 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -1618,8 +1624,10 @@ public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> 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);
}
}
Expand All @@ -1632,7 +1640,7 @@ public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> 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;
}

/**
Expand Down Expand Up @@ -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<Method> {

/**
* 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;
}
}
}
}
Expand Down

0 comments on commit aaeb22e

Please sign in to comment.