Skip to content

Commit

Permalink
[GR-14773] [GR-15367] [GR-15370] Enhancements, fixes and tests for na…
Browse files Browse the repository at this point in the history
…tive-image-agent and native-image-configure.

PullRequest: graal/3491
  • Loading branch information
peter-hofer committed Apr 30, 2019
2 parents d39ee97 + 3f8d7d5 commit 8c84d1e
Show file tree
Hide file tree
Showing 21 changed files with 753 additions and 454 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
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.agent.restrict.TypeAccessChecker;
import com.oracle.svm.configure.config.ConfigurationSet;
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.configure.trace.AccessAdvisor;
Expand Down Expand Up @@ -233,10 +234,12 @@ public static int onLoad(JNIJavaVM vm, CCharPointer options, @SuppressWarnings("
callbacks.setThreadEnd(onThreadEndLiteral.getFunctionPointer());

accessAdvisor = new AccessAdvisor();
TypeAccessChecker reflectAccessChecker = null;
try {
ReflectAccessVerifier verifier = null;
if (!restrictConfigs.getReflectConfigPaths().isEmpty()) {
verifier = new ReflectAccessVerifier(restrictConfigs.loadReflectConfig(ConfigurationSet.FAIL_ON_EXCEPTION), accessAdvisor);
reflectAccessChecker = new TypeAccessChecker(restrictConfigs.loadReflectConfig(ConfigurationSet.FAIL_ON_EXCEPTION));
verifier = new ReflectAccessVerifier(reflectAccessChecker, accessAdvisor);
}
ProxyAccessVerifier proxyVerifier = null;
if (!restrictConfigs.getProxyConfigPaths().isEmpty()) {
Expand All @@ -254,7 +257,8 @@ public static int onLoad(JNIJavaVM vm, CCharPointer options, @SuppressWarnings("
try {
JniAccessVerifier verifier = null;
if (!restrictConfigs.getJniConfigPaths().isEmpty()) {
verifier = new JniAccessVerifier(restrictConfigs.loadJniConfig(ConfigurationSet.FAIL_ON_EXCEPTION), accessAdvisor);
TypeAccessChecker accessChecker = new TypeAccessChecker(restrictConfigs.loadJniConfig(ConfigurationSet.FAIL_ON_EXCEPTION));
verifier = new JniAccessVerifier(accessChecker, reflectAccessChecker, accessAdvisor);
}
JniCallInterceptor.onLoad(traceWriter, verifier);
} catch (Throwable t) {
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.WordPointer;
Expand Down Expand Up @@ -293,22 +294,32 @@ public static String getClassNameOrNull(JNIEnvironment env, JNIObjectHandle claz
return getClassNameOr(env, clazz, null, null);
}

static JNIObjectHandle getMethodDeclaringClass(JNIMethodId method) {
public static JNIObjectHandle getMethodDeclaringClass(JNIMethodId method) {
WordPointer declaringClass = StackValue.get(WordPointer.class);
if (method.isNull() || jvmtiFunctions().GetMethodDeclaringClass().invoke(jvmtiEnv(), method, declaringClass) != JvmtiError.JVMTI_ERROR_NONE) {
declaringClass.write(nullPointer());
}
return declaringClass.read();
}

static JNIObjectHandle getFieldDeclaringClass(JNIObjectHandle clazz, JNIFieldId method) {
public static JNIObjectHandle getFieldDeclaringClass(JNIObjectHandle clazz, JNIFieldId method) {
WordPointer declaringClass = StackValue.get(WordPointer.class);
if (method.isNull() || jvmtiFunctions().GetFieldDeclaringClass().invoke(jvmtiEnv(), clazz, method, declaringClass) != JvmtiError.JVMTI_ERROR_NONE) {
declaringClass.write(nullPointer());
}
return declaringClass.read();
}

public static String getFieldName(JNIObjectHandle clazz, JNIFieldId field) {
String name = null;
CCharPointerPointer namePtr = StackValue.get(CCharPointerPointer.class);
if (jvmtiFunctions().GetFieldName().invoke(jvmtiEnv(), clazz, field, namePtr, nullPointer(), nullPointer()) == JvmtiError.JVMTI_ERROR_NONE) {
name = fromCString(namePtr.read());
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), namePtr.read());
}
return name;
}

public static boolean clearException(JNIEnvironment localEnv) {
if (jniFunctions().getExceptionCheck().invoke(localEnv)) {
jniFunctions().getExceptionClear().invoke(localEnv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ private Map<String, Object> arraysToLists(Map<String, Object> map) {
}

private List<?> arraysToLists(Object[] array) {
for (int i = 0; i < array.length; i++) {
if (array[i] instanceof Object[]) {
array[i] = arraysToLists((Object[]) array[i]);
Object[] newArray = Arrays.copyOf(array, array.length);
for (int i = 0; i < newArray.length; i++) {
if (newArray[i] instanceof Object[]) {
newArray[i] = arraysToLists((Object[]) newArray[i]);
}
}
return Arrays.asList(array);
return Arrays.asList(newArray);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.oracle.svm.agent;

import java.io.Closeable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -36,7 +37,24 @@ public abstract class TraceWriter implements Closeable {
public static final String UNKNOWN_VALUE = new String("\0");

static Object handleSpecialValue(Object obj) {
return (obj == EXPLICIT_NULL) ? null : obj;
if (obj == EXPLICIT_NULL) {
return null;
}
if (obj instanceof Object[]) {
Object[] array = (Object[]) obj;
Object[] newArray = null;
for (int i = 0; i < array.length; i++) {
Object newValue = handleSpecialValue(array[i]);
if (newValue != array[i]) {
if (newArray == null) {
newArray = Arrays.copyOf(array, array.length);
}
newArray[i] = newValue;
}
}
return (newArray != null) ? newArray : array;
}
return obj;
}

void traceInitialization() {
Expand Down Expand Up @@ -86,8 +104,8 @@ public void traceCall(String tracer, String function, Object clazz, Object decla
if (result != null) {
entry.put("result", handleSpecialValue(result));
}
if (args != null && args.length > 0) {
entry.put("args", args);
if (args != null) {
entry.put("args", handleSpecialValue(args));
}
traceEntry(entry);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,163 +24,20 @@
*/
package com.oracle.svm.agent.restrict;

import static com.oracle.svm.agent.Support.fromCString;
import static com.oracle.svm.agent.Support.getClassNameOrNull;
import static com.oracle.svm.agent.Support.jniFunctions;
import static com.oracle.svm.agent.Support.jvmtiEnv;
import static com.oracle.svm.agent.Support.jvmtiFunctions;
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
import static org.graalvm.word.WordFactory.nullPointer;

import java.lang.reflect.Modifier;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.WordPointer;

import com.oracle.svm.agent.jvmti.JvmtiError;
import com.oracle.svm.configure.config.ConfigurationMethod;
import com.oracle.svm.configure.config.ConfigurationType;
import com.oracle.svm.configure.config.TypeConfiguration;
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;

class AbstractAccessVerifier {
protected final TypeConfiguration configuration;
protected final AccessAdvisor accessAdvisor;

AbstractAccessVerifier(TypeConfiguration configuration, AccessAdvisor advisor) {
this.configuration = configuration;
AbstractAccessVerifier(AccessAdvisor advisor) {
this.accessAdvisor = advisor;
}

protected boolean shouldApproveWithoutChecks(JNIEnvironment env, JNIObjectHandle callerClass) {
return accessAdvisor.shouldIgnore(() -> getClassNameOrNull(env, callerClass));
}

protected boolean isFieldAccessible(JNIEnvironment env, JNIObjectHandle clazz, Supplier<String> name, JNIFieldId field, JNIObjectHandle declaring) {
ConfigurationType declaringType = getType(declaring);
if (declaringType != null) {
if (declaringType.haveAllDeclaredFields() || declaringType.hasIndividualField((name.get()))) {
return true;
}
}
/*
* Check if the field is public and if so, whether `clazz` or any of its superclasses or
* superinterfaces which are subtypes of `declaring` have "allPublicFields" set on them.
*/
CIntPointer modifiers = StackValue.get(CIntPointer.class);
if (jvmtiFunctions().GetFieldModifiers().invoke(jvmtiEnv(), clazz, field, modifiers) == JvmtiError.JVMTI_ERROR_NONE) {
// Checkstyle: allow reflection
boolean isPublic = (modifiers.read() & Modifier.PUBLIC) != 0;
if (isPublic) {
if (declaringType != null && declaringType.haveAllPublicFields()) {
return true;
}
if (declaring.notEqual(clazz)) { // shortcut for declaring-only lookups
JNIObjectHandle current = clazz;
do {
ConfigurationType currentType = getType(current);
if (currentType != null && currentType.haveAllPublicFields()) {
return true;
}
if (testInterfacesBetween(env, declaring, current, ConfigurationType::haveAllPublicFields)) {
return true;
}
current = jniFunctions().getGetSuperclass().invoke(env, current);
} while (current.notEqual(nullHandle()) && jniFunctions().getIsAssignableFrom().invoke(env, current, declaring));
}
}
}
return false;
}

protected boolean isMethodAccessible(JNIEnvironment env, JNIObjectHandle clazz, String name, Supplier<String> signature, JNIMethodId method, JNIObjectHandle declaring) {
boolean isConstructor = ConfigurationMethod.CONSTRUCTOR_NAME.equals(name);
ConfigurationType declaringType = getType(declaring);
if (declaringType != null) {
boolean haveAllDeclared = isConstructor ? declaringType.haveAllDeclaredConstructors() : declaringType.haveAllDeclaredMethods();
if (haveAllDeclared || declaringType.hasIndividualMethod(name, signature.get())) {
return true;
}
}
/*
* Check if the method is public and not static, and if so, whether `clazz` or any of its
* superclasses or superinterfaces which are subtypes of `declaring` have "allPublicMethods"
* set on them.
*/
CIntPointer modifiers = StackValue.get(CIntPointer.class);
if (jvmtiFunctions().GetMethodModifiers().invoke(jvmtiEnv(), method, modifiers) == JvmtiError.JVMTI_ERROR_NONE) {
// Checkstyle: allow reflection
boolean isPublic = (modifiers.read() & Modifier.PUBLIC) != 0;
if (isPublic && declaringType != null) {
if (isConstructor ? declaringType.haveAllPublicConstructors() : declaringType.haveAllPublicMethods()) {
return true;
}
}
boolean isStatic = (modifiers.read() & Modifier.STATIC) != 0;
if (isPublic && !isStatic && !isConstructor && declaring.notEqual(clazz)) {
JNIObjectHandle current = clazz;
do {
ConfigurationType currentType = getType(current);
if (currentType != null && currentType.haveAllPublicMethods()) {
return true;
}
if (testInterfacesBetween(env, declaring, current, ConfigurationType::haveAllPublicMethods)) {
return true;
}
current = jniFunctions().getGetSuperclass().invoke(env, current);
} while (current.notEqual(nullHandle()) && jniFunctions().getIsAssignableFrom().invoke(env, current, declaring));
}
}
// NOTE: this method might override a virtual method in a superclass which is
// registered, but we still deny access because it really is a different method.
return false;
}

protected ConfigurationType getType(JNIObjectHandle clazz) {
WordPointer signaturePtr = StackValue.get(WordPointer.class);
if (jvmtiFunctions().GetClassSignature().invoke(jvmtiEnv(), clazz, signaturePtr, nullPointer()) == JvmtiError.JVMTI_ERROR_NONE) {
String className = fromCString(signaturePtr.read());
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), signaturePtr.read());
return configuration.getByInternalName(className);
}
return null;
}

/**
* Determines, for interfaces implemented by {@code clazz} which are assignable to
* {@code declaring}, if {@code predicate} returns {@code true}, returning on the first match.
*/
protected boolean testInterfacesBetween(JNIEnvironment env, JNIObjectHandle declaring, JNIObjectHandle clazz, Predicate<ConfigurationType> predicate) {
CIntPointer ifaceCountPtr = StackValue.get(CIntPointer.class);
WordPointer ifaceArrayPtr = StackValue.get(WordPointer.class);
// NOTE: GetImplementedInterfaces provides only direct superinterfaces, so we recurse
if (jvmtiFunctions().GetImplementedInterfaces().invoke(jvmtiEnv(), clazz, ifaceCountPtr, ifaceArrayPtr) == JvmtiError.JVMTI_ERROR_NONE) {
WordPointer ifaceArray = ifaceArrayPtr.read();
try {
int ifaceCount = ifaceCountPtr.read();
for (int i = 0; i < ifaceCount; i++) {
JNIObjectHandle iface = ifaceArray.read(i);
if (jniFunctions().getIsAssignableFrom().invoke(env, iface, declaring)) {
ConfigurationType ifaceType = getType(iface);
if (ifaceType != null && predicate.test(ifaceType)) {
return true;
}
if (testInterfacesBetween(env, declaring, iface, predicate)) {
return true;
}
}
}
} finally {
jvmtiFunctions().Deallocate().invoke(jvmtiEnv(), ifaceArray);
}
}
return false;
}
}
Loading

0 comments on commit 8c84d1e

Please sign in to comment.