< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
@@ -23,10 +23,13 @@
* questions.
*/
package java.lang.invoke;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.IllegalAccessLogger;
+import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyAccess;
@@ -41,10 +44,13 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Module;
import java.lang.reflect.ReflectPermission;
import java.nio.ByteOrder;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
@@ -189,10 +195,16 @@
if (!targetModule.isOpen(pn, callerModule))
throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
}
if ((lookup.lookupModes() & Lookup.MODULE) == 0)
throw new IllegalAccessException("lookup does not have MODULE lookup mode");
+ if (!callerModule.isNamed() && targetModule.isNamed()) {
+ IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
+ if (logger != null) {
+ logger.logIfOpenedByBackdoor(lookup, targetClass);
+ }
+ }
return new Lookup(targetClass);
}
/**
* Performs an unchecked "crack" of a
@@ -853,10 +865,116 @@
}
if (newModes == oldModes) return this; // return self if no change
return new Lookup(lookupClass(), newModes);
}
+ /**
+ * Defines a class to the same class loader and in the same runtime package and
+ * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
+ * {@linkplain #lookupClass() lookup class}.
+ *
+ * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must include
+ * {@link #PACKAGE PACKAGE} access as default (package) members will be
+ * accessible to the class. The {@code PACKAGE} lookup mode serves to authenticate
+ * that the lookup object was created by a caller in the runtime package (or derived
+ * from a lookup originally created by suitably privileged code to a target class in
+ * the runtime package). The lookup modes cannot include {@link #PRIVATE PRIVATE}
+ * access. A lookup with {@code PRIVATE} access can be downgraded to drop this lookup
+ * mode with the {@linkplain #dropLookupMode(int) dropLookupMode} method. </p>
+ *
+ * <p> The {@code bytes} parameter is the class bytes of a valid class file (as defined
+ * by the <em>The Java Virtual Machine Specification</em>) with a class name in the
+ * same package as the lookup class. </p>
+ *
+ * <p> This method does not run the class initializer. The class initializer may
+ * run at a later time, as detailed in section 12.4 of the <em>The Java Language
+ * Specification</em>. </p>
+ *
+ * <p> If there is a security manager, its {@code checkPermission} method is first called
+ * to check {@code RuntimePermission("defineClass")}. </p>
+ *
+ * @param bytes the class bytes
+ * @return the {@code Class} object for the class
+ * @throws IllegalArgumentException the bytes are for a class in a different package
+ * to the lookup class
+ * @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
+ * @throws UnsupportedOperationException if the lookup class has {@code PRIVATE} access
+ * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be
+ * verified ({@code VerifyError}), is already defined, or another linkage error occurs
+ * @throws SecurityException if denied by the security manager
+ * @throws NullPointerException if {@code bytes} is {@code null}
+ * @since 9
+ * @spec JPMS
+ * @see Lookup#privateLookupIn
+ * @see Lookup#dropLookupMode
+ * @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
+ */
+ public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("defineClass"));
+ if (hasPrivateAccess())
+ throw new UnsupportedOperationException("PRIVATE access not supported");
+ if ((lookupModes() & PACKAGE) == 0)
+ throw new IllegalAccessException("Lookup does not have PACKAGE access");
+ assert (lookupModes() & (MODULE|PUBLIC)) != 0;
+
+ // parse class bytes to get class name (in internal form)
+ bytes = bytes.clone();
+ String name;
+ try {
+ ClassReader reader = new ClassReader(bytes);
+ name = reader.getClassName();
+ } catch (RuntimeException e) {
+ // ASM exceptions are poorly specified
+ ClassFormatError cfe = new ClassFormatError();
+ cfe.initCause(e);
+ throw cfe;
+ }
+
+ // get package and class name in binary form
+ String cn, pn;
+ int index = name.lastIndexOf('/');
+ if (index == -1) {
+ cn = name;
+ pn = "";
+ } else {
+ cn = name.replace('/', '.');
+ pn = cn.substring(0, index);
+ }
+ if (!pn.equals(lookupClass.getPackageName())) {
+ throw new IllegalArgumentException("Class not in same package as lookup class");
+ }
+
+ // invoke the class loader's defineClass method
+ ClassLoader loader = lookupClass.getClassLoader();
+ ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null;
+ String source = "__Lookup_defineClass__";
+ Class<?> clazz = SharedSecrets.getJavaLangAccess().defineClass(loader, cn, bytes, pd, source);
+ assert clazz.getClassLoader() == lookupClass.getClassLoader()
+ && clazz.getPackageName().equals(lookupClass.getPackageName())
+ && protectionDomain(clazz) == lookupClassProtectionDomain();
+ return clazz;
+ }
+
+ private ProtectionDomain lookupClassProtectionDomain() {
+ ProtectionDomain pd = cachedProtectionDomain;
+ if (pd == null) {
+ cachedProtectionDomain = pd = protectionDomain(lookupClass);
+ }
+ return pd;
+ }
+
+ private ProtectionDomain protectionDomain(Class<?> clazz) {
+ PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
+ return AccessController.doPrivileged(pa);
+ }
+
+ // cached protection domain
+ private volatile ProtectionDomain cachedProtectionDomain;
+
+
// Make sure outer class is initialized first.
static { IMPL_NAMES.getClass(); }
/** Package-private version of lookup which is trusted. */
static final Lookup IMPL_LOOKUP = new Lookup(Object.class, TRUSTED);
@@ -1946,11 +2064,11 @@
return callerClass;
}
/**
* Returns {@code true} if this lookup has {@code PRIVATE} access.
- * @return {@code true} if this lookup has {@code PRIVATE} acesss.
+ * @return {@code true} if this lookup has {@code PRIVATE} access.
* @since 9
*/
public boolean hasPrivateAccess() {
return (allowedModes & PRIVATE) != 0;
}
< prev index next >