Skip to content

Commit

Permalink
[GR-22541] [GR-22544] [GR-22655] Capture agent events during class in…
Browse files Browse the repository at this point in the history
…itialization, enable class loader support by default, broader filter for Libgraal/JVMCI classes.

PullRequest: graal/5965
  • Loading branch information
peter-hofer committed Apr 21, 2020
2 parents 849ea8a + b37ae04 commit 9abecf7
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.agent;

import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
import static com.oracle.svm.jvmtiagentbase.Support.check;
import static com.oracle.svm.jvmtiagentbase.Support.checkJni;
import static com.oracle.svm.jvmtiagentbase.Support.checkNoException;
Expand All @@ -45,8 +47,6 @@
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT;
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_CLASS_PREPARE;
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_NATIVE_METHOD_BIND;
import static com.oracle.svm.core.util.VMError.guarantee;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
import static org.graalvm.word.WordFactory.nullPointer;

import java.nio.ByteBuffer;
Expand Down Expand Up @@ -78,6 +78,17 @@
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.WordFactory;

import com.oracle.svm.agent.restrict.ProxyAccessVerifier;
import com.oracle.svm.agent.restrict.ReflectAccessVerifier;
import com.oracle.svm.agent.restrict.ResourceAccessVerifier;
import com.oracle.svm.configure.config.ConfigurationMethod;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNINativeMethod;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jni.nativeapi.JNIValue;
import com.oracle.svm.jvmtiagentbase.AgentIsolate;
import com.oracle.svm.jvmtiagentbase.ConstantPoolTool;
import com.oracle.svm.jvmtiagentbase.ConstantPoolTool.MethodReference;
Expand All @@ -90,17 +101,6 @@
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventMode;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiFrameInfo;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiLocationFormat;
import com.oracle.svm.agent.restrict.ProxyAccessVerifier;
import com.oracle.svm.agent.restrict.ReflectAccessVerifier;
import com.oracle.svm.agent.restrict.ResourceAccessVerifier;
import com.oracle.svm.configure.config.ConfigurationMethod;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNINativeMethod;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jni.nativeapi.JNIValue;

import jdk.vm.ci.meta.MetaUtil;

Expand Down Expand Up @@ -182,15 +182,11 @@ private static boolean forName(JNIEnvironment jni, Breakpoint bp) {
boolean allowed = (accessVerifier == null || accessVerifier.verifyForName(jni, callerClass, className));
Object result = false;
if (allowed) {
boolean initializeValid = true;
boolean classLoaderValid = true;
CIntPointer initializePtr = StackValue.get(CIntPointer.class);
WordPointer classLoaderPtr = StackValue.get(WordPointer.class);
if (bp.method == agent.handles().javaLangClassForName3) {
initializeValid = (jvmtiFunctions().GetLocalInt().invoke(jvmtiEnv(), nullHandle(), 0, 1, initializePtr) == JvmtiError.JVMTI_ERROR_NONE);
classLoaderValid = (jvmtiFunctions().GetLocalObject().invoke(jvmtiEnv(), nullHandle(), 0, 2, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE);
} else {
initializePtr.write(1);
classLoaderPtr.write(nullHandle());
if (callerClass.notEqual(nullHandle())) {
/*
Expand All @@ -202,8 +198,13 @@ private static boolean forName(JNIEnvironment jni, Breakpoint bp) {
}
}
result = TraceWriter.UNKNOWN_VALUE;
if (initializeValid && classLoaderValid) {
result = nullHandle().notEqual(Support.callStaticObjectMethodLIL(jni, bp.clazz, agent.handles().javaLangClassForName3, name, initializePtr.read(), classLoaderPtr.read()));
if (classLoaderValid) {
/*
* Even if the original call requested class initialization, disable it because
* recursion checks keep us from seeing events of interest during initialization.
*/
int initialize = 0;
result = nullHandle().notEqual(Support.callStaticObjectMethodLIL(jni, bp.clazz, agent.handles().javaLangClassForName3, name, initialize, classLoaderPtr.read()));
if (clearException(jni)) {
result = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
boolean builtinCallerFilter = true;
boolean builtinHeuristicFilter = true;
List<String> callerFilterFiles = new ArrayList<>();
boolean experimentalClassLoaderSupport = false;
boolean experimentalClassLoaderSupport = true;
boolean build = false;
int configWritePeriod = -1; // in seconds
int configWritePeriodInitialDelay = 1; // in seconds
Expand Down Expand Up @@ -179,6 +179,8 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
callerFilterFiles.add(getTokenValue(token));
} else if (token.equals("experimental-class-loader-support")) {
experimentalClassLoaderSupport = true;
} else if (token.startsWith("experimental-class-loader-support=")) {
experimentalClassLoaderSupport = Boolean.parseBoolean(getTokenValue(token));
} else if (token.startsWith("config-write-period-secs=")) {
configWritePeriod = parseIntegerOrNegative(getTokenValue(token));
if (configWritePeriod <= 0) {
Expand Down Expand Up @@ -213,7 +215,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
}
if (!callerFilterFiles.isEmpty()) {
if (callersFilter == null) {
callersFilter = AccessAdvisor.copyBuiltinFilterTree();
callersFilter = AccessAdvisor.copyBuiltinCallerFilterTree();
}
for (String path : callerFilterFiles) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ class AbstractAccessVerifier {
this.accessAdvisor = advisor;
}

protected boolean shouldApproveWithoutChecks(JNIEnvironment env, JNIObjectHandle callerClass) {
return accessAdvisor.shouldIgnoreCaller(lazyClassNameOrNull(env, callerClass));
protected boolean shouldApproveWithoutChecks(JNIEnvironment env, JNIObjectHandle queriedClass, JNIObjectHandle callerClass) {
return shouldApproveWithoutChecks(lazyClassNameOrNull(env, queriedClass), lazyClassNameOrNull(env, callerClass));
}

protected boolean shouldApproveWithoutChecks(LazyValue<String> queriedClassName, LazyValue<String> callerClassName) {
return accessAdvisor.shouldIgnore(queriedClassName, callerClassName);
}

protected static LazyValue<String> lazyClassNameOrNull(JNIEnvironment env, JNIObjectHandle clazz) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,27 @@
import static com.oracle.svm.configure.trace.LazyValueUtils.lazyGet;
import static com.oracle.svm.configure.trace.LazyValueUtils.lazyValue;

import org.graalvm.compiler.phases.common.LazyValue;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
import org.graalvm.nativeimage.c.type.WordPointer;

import com.oracle.svm.agent.NativeImageAgent;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiError;
import com.oracle.svm.configure.config.ConfigurationMethod;
import com.oracle.svm.configure.trace.AccessAdvisor;
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
import com.oracle.svm.jni.nativeapi.JNIFieldId;
import com.oracle.svm.jni.nativeapi.JNIMethodId;
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiError;

import jdk.vm.ci.meta.MetaUtil;

/**
* In restriction mode, decides whether to permit or deny individual accesses via JNI, using
* {@link AccessAdvisor} for additional exemptions from its own rules, such as system classes.
*/
public class JniAccessVerifier extends AbstractAccessVerifier {
private final TypeAccessChecker typeAccessChecker;
private final TypeAccessChecker reflectTypeAccessChecker;
Expand All @@ -61,39 +68,57 @@ public JniAccessVerifier(TypeAccessChecker typeAccessChecker, TypeAccessChecker

@SuppressWarnings("unused")
public boolean verifyDefineClass(JNIEnvironment env, CCharPointer name, JNIObjectHandle loader, CCharPointer buf, int bufLen, JNIObjectHandle callerClass) {
if (shouldApproveWithoutChecks(env, callerClass)) {
LazyValue<String> javaName = lazyConvertFindClassName(name);
if (shouldApproveWithoutChecks(javaName, lazyClassNameOrNull(env, callerClass))) {
return true;
}
try (CCharPointerHolder message = toCString(NativeImageAgent.MESSAGE_PREFIX + "defining classes is not permitted.")) {
try (CCharPointerHolder message = toCString(NativeImageAgent.MESSAGE_PREFIX + "defining classes is not permitted: " + javaName.get())) {
// SecurityException seems most fitting from the exceptions allowed by the JNI spec
jniFunctions().getThrowNew().invoke(env, agent.handles().javaLangSecurityException, message.get());
}
return false;
}

public boolean verifyFindClass(JNIEnvironment env, CCharPointer cname, JNIObjectHandle callerClass) {
if (shouldApproveWithoutChecks(env, callerClass)) {
public boolean verifyFindClass(JNIEnvironment env, CCharPointer name, JNIObjectHandle callerClass) {
LazyValue<String> javaName = lazyConvertFindClassName(name);
if (shouldApproveWithoutChecks(javaName, lazyClassNameOrNull(env, callerClass))) {
return true;
}
String name = fromCString(cname);
if (name != null) {
if (!name.startsWith("[") && name.length() > 1) {
name = "L" + name + ";"; // FindClass doesn't require those
}
if (typeAccessChecker.getConfiguration().getByInternalName(name) != null) {
return true;
}
if (javaName.get() != null && typeAccessChecker.getConfiguration().get(javaName.get()) != null) {
return true;
}
try (CCharPointerHolder message = toCString(NativeImageAgent.MESSAGE_PREFIX + "configuration does not permit access to class: " + name)) {
try (CCharPointerHolder message = toCString(NativeImageAgent.MESSAGE_PREFIX + "configuration does not permit access to class: " + javaName.get())) {
jniFunctions().getThrowNew().invoke(env, agent.handles().javaLangNoClassDefFoundError, message.get());
}
return false;
}

private static LazyValue<String> lazyConvertFindClassName(CCharPointer name) {
return lazyGet(() -> {
String s = fromCString(name);
if (s != null) {
if (!s.startsWith("[") && s.length() > 1) {
s = "L" + s + ";";
}
try {
return MetaUtil.internalNameToJava(s, true, true);
} catch (Exception ignored) {
// likely malformed input from the observed application
}
}
return null;
});
}

public boolean verifyGetMethodID(JNIEnvironment env, JNIObjectHandle clazz, CCharPointer cname, CCharPointer csignature, JNIMethodId result, JNIObjectHandle callerClass) {
LazyValue<String> clazzName = lazyClassNameOrNull(env, clazz);
LazyValue<String> callerClassName = lazyClassNameOrNull(env, callerClass);
if (shouldApproveWithoutChecks(clazzName, callerClassName)) {
return true;
}
assert result.isNonNull();
String name = fromCString(cname);
if (accessAdvisor.shouldIgnoreJniMethodLookup(lazyClassNameOrNull(env, clazz), lazyValue(name), lazyGet(() -> fromCString(csignature)), lazyClassNameOrNull(env, callerClass))) {
if (accessAdvisor.shouldIgnoreJniMethodLookup(clazzName, lazyValue(name), lazyGet(() -> fromCString(csignature)), callerClassName)) {
return true;
}
WordPointer declaringPtr = StackValue.get(WordPointer.class);
Expand All @@ -115,7 +140,7 @@ public boolean verifyGetFieldID(JNIEnvironment env, JNIObjectHandle clazz, CChar
@SuppressWarnings("unused") CCharPointer csignature, JNIFieldId result, JNIObjectHandle callerClass) {

assert result.isNonNull();
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(env, clazz, callerClass)) {
return true;
}
// Check if the member in the declaring method is registered.
Expand All @@ -134,9 +159,13 @@ public boolean verifyGetFieldID(JNIEnvironment env, JNIObjectHandle clazz, CChar
}

public boolean verifyThrowNew(JNIEnvironment env, JNIObjectHandle clazz, JNIObjectHandle callerClass) {
LazyValue<String> callerClassName = lazyClassNameOrNull(env, callerClass);
if (shouldApproveWithoutChecks(lazyClassNameOrNull(env, clazz), callerClassName)) {
return true;
}
String name = ConfigurationMethod.CONSTRUCTOR_NAME;
String signature = "(Ljava/lang/String;)V";
if (accessAdvisor.shouldIgnoreJniMethodLookup(lazyClassNameOrNull(env, clazz), lazyValue(name), lazyValue(signature), lazyClassNameOrNull(env, callerClass))) {
if (accessAdvisor.shouldIgnoreJniMethodLookup(lazyClassNameOrNull(env, clazz), lazyValue(name), lazyValue(signature), callerClassName)) {
return true;
}
JNIMethodId result;
Expand All @@ -149,15 +178,15 @@ public boolean verifyThrowNew(JNIEnvironment env, JNIObjectHandle clazz, JNIObje

public boolean verifyFromReflectedMethod(JNIEnvironment env, JNIObjectHandle declaring, String name, String signature, JNIMethodId result, JNIObjectHandle callerClass) {
assert result.isNonNull();
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(env, declaring, callerClass)) {
return true;
}
return typeAccessChecker.isMethodAccessible(env, declaring, name, () -> signature, result, declaring);
}

public boolean verifyFromReflectedField(JNIEnvironment env, JNIObjectHandle declaring, String name, JNIFieldId result, JNIObjectHandle callerClass) {
assert result.isNonNull();
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(env, declaring, callerClass)) {
return true;
}
return typeAccessChecker.isFieldAccessible(env, declaring, () -> name, result, declaring);
Expand All @@ -168,7 +197,7 @@ public boolean verifyToReflectedMethod(JNIEnvironment env, JNIObjectHandle clazz
if (reflectTypeAccessChecker == null) {
return true;
}
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(env, clazz, callerClass)) {
return true;
}
return reflectTypeAccessChecker.isMethodAccessible(env, clazz, name, () -> signature, methodId, declaring);
Expand All @@ -179,20 +208,16 @@ public boolean verifyToReflectedField(JNIEnvironment env, JNIObjectHandle clazz,
if (reflectTypeAccessChecker == null) {
return true;
}
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(env, clazz, callerClass)) {
return true;
}
return reflectTypeAccessChecker.isFieldAccessible(env, clazz, () -> name, fieldId, declaring);
}

public boolean verifyNewObjectArray(JNIEnvironment env, JNIObjectHandle arrayClass, JNIObjectHandle callerClass) {
if (shouldApproveWithoutChecks(env, callerClass)) {
return true;
}
if (accessAdvisor.shouldIgnoreJniNewObjectArray(lazyClassNameOrNull(env, arrayClass), lazyClassNameOrNull(env, callerClass))) {
if (shouldApproveWithoutChecks(env, arrayClass, callerClass)) {
return true;
}
return typeAccessChecker.getType(arrayClass) != null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.agent.restrict;

import static com.oracle.svm.configure.trace.LazyValueUtils.lazyNull;

import java.util.Arrays;

import com.oracle.svm.configure.config.ProxyConfiguration;
Expand All @@ -48,7 +50,7 @@ public boolean verifyGetProxyClass(JNIEnvironment env, Object interfaceNames, JN
}

private boolean verifyProxyAccess(JNIEnvironment env, Object interfaceNames, JNIObjectHandle callerClass) {
if (shouldApproveWithoutChecks(env, callerClass)) {
if (shouldApproveWithoutChecks(lazyNull(), lazyClassNameOrNull(env, callerClass))) {
return true;
}
return (interfaceNames instanceof String[]) && configuration.contains(Arrays.asList((String[]) interfaceNames));
Expand Down
Loading

0 comments on commit 9abecf7

Please sign in to comment.