Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_NATIVE_METHOD_BIND;
import static org.graalvm.word.WordFactory.nullPointer;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
Expand Down Expand Up @@ -80,6 +81,7 @@
import com.oracle.svm.agent.stackaccess.EagerlyLoadedJavaStackAccess;
import com.oracle.svm.agent.stackaccess.InterceptedState;
import com.oracle.svm.agent.tracing.core.Tracer;
import com.oracle.svm.configure.ClassNameSupport;
import com.oracle.svm.configure.LambdaConfigurationTypeDescriptor;
import com.oracle.svm.configure.NamedConfigurationTypeDescriptor;
import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor;
Expand Down Expand Up @@ -263,8 +265,17 @@ static Object getTypeDescriptor(JNIEnvironment env, JNIObjectHandle clazz) {
}

private static boolean forName(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle name = getObjectArgument(thread, 0);
return handleForName(jni, bp, state, name);
}

private static boolean forNameWithModule(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle name = getObjectArgument(thread, 1);
return handleForName(jni, bp, state, name);
}

private static boolean handleForName(JNIEnvironment jni, Breakpoint bp, InterceptedState state, JNIObjectHandle name) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
String className = fromJniString(jni, name);
if (className == null) {
return true; /* No point in tracing this. */
Expand All @@ -273,6 +284,14 @@ private static boolean forName(JNIEnvironment jni, JNIObjectHandle thread, Break
return true;
}

private static boolean arrayType(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle componentType = getReceiver(thread);
String arrayTypeName = ClassNameSupport.getArrayReflectionName(getClassNameOrNull(jni, componentType));
traceReflectBreakpoint(jni, bp.clazz, nullHandle(), callerClass, "forName", null, state.getFullStackTraceOrNull(), arrayTypeName);
return true;
}

private static boolean getFields(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetFields(jni, thread, bp, state);
}
Expand Down Expand Up @@ -543,6 +562,34 @@ private static boolean getEnclosingMethod(JNIEnvironment jni, JNIObjectHandle th
return true;
}

private static boolean accessField(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {
JNIObjectHandle field = getReceiver(thread);
return accessFieldInternal(jni, state, field);
}

private static boolean accessFieldUnsafe(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {
JNIObjectHandle field = getObjectArgument(thread, 1);
return accessFieldInternal(jni, state, field);
}

private static boolean accessFieldInternal(JNIEnvironment jni, InterceptedState state, JNIObjectHandle field) {
JNIObjectHandle callerClass = state.getDirectCallerClass();

JNIObjectHandle declaring = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetDeclaringClass);
if (clearException(jni)) {
declaring = nullHandle();
}

JNIObjectHandle nameHandle = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetName);
if (clearException(jni)) {
nameHandle = nullHandle();
}
String name = fromJniString(jni, nameHandle);

traceReflectBreakpoint(jni, declaring, declaring, callerClass, "accessField", declaring.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name);
return true;
}

private static boolean invokeMethod(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleInvokeMethod(jni, thread, bp, state, true);
}
Expand Down Expand Up @@ -582,6 +629,27 @@ private static boolean handleInvokeMethod(JNIEnvironment jni, JNIObjectHandle th
JNIObjectHandle clazz = getObjectArgument(thread, 1);
traceReflectBreakpoint(jni, clazz, nullHandle(), callerClass, "newInstance", clazz.notEqual(nullHandle()), state.getFullStackTraceOrNull());
}

/*
* Calling Field.get/set and associated methods through Method.invoke should register the
* field for reflective access
*/
if (isInvoke && isFieldAccess(jni, declaring, name)) {
JNIObjectHandle field = getObjectArgument(thread, 1);

JNIObjectHandle clazz = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetDeclaringClass);
if (clearException(jni)) {
clazz = nullHandle();
}

JNIObjectHandle fieldNameHandle = Support.callObjectMethod(jni, field, agent.handles().javaLangReflectMemberGetName);
if (clearException(jni)) {
clazz = nullHandle();
}
String fieldName = fromJniString(jni, fieldNameHandle);

traceReflectBreakpoint(jni, clazz, nullHandle(), callerClass, "accessField", clazz.notEqual(nullHandle()), state.getFullStackTraceOrNull(), fieldName);
}
return true;
}

Expand All @@ -597,6 +665,24 @@ private static boolean isClassNewInstance(JNIEnvironment jni, JNIObjectHandle de
return "java.lang.Class".equals(className);
}

/**
* A call is a field access iff it is a call to a method from {@link Field} whose name starts
* with "get" or "set".
*/
private static boolean isFieldAccess(JNIEnvironment jni, JNIObjectHandle declaring, String name) {
if (declaring.equal(nullHandle()) || name == null || (!name.startsWith("get") && !name.startsWith("set"))) {
return false;
}

JNIObjectHandle classNameHandle = Support.callObjectMethod(jni, declaring, agent.handles().javaLangClassGetName);
if (clearException(jni)) {
classNameHandle = nullHandle();
}
String className = fromJniString(jni, classNameHandle);

return Field.class.getName().equals(className);
}

private static boolean invokeConstructor(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleInvokeConstructor(jni, bp, state, getReceiver(thread));
}
Expand Down Expand Up @@ -1650,6 +1736,7 @@ private interface BreakpointHandler {
private static final BreakpointSpecification[] BREAKPOINT_SPECIFICATIONS = {
brk("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::forName),
brk("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", BreakpointInterceptor::forName),
brk("java/lang/Class", "forName", "(Ljava/lang/Module;Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::forNameWithModule),

brk("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;", BreakpointInterceptor::getFields),
brk("java/lang/Class", "getClasses", "()[Ljava/lang/Class;", BreakpointInterceptor::getClasses),
Expand All @@ -1673,6 +1760,7 @@ private interface BreakpointHandler {
brk("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", BreakpointInterceptor::getConstructor),

brk("java/lang/Class", "newInstance", "()Ljava/lang/Object;", BreakpointInterceptor::newInstance),
brk("java/lang/Class", "arrayType", "()Ljava/lang/Class;", BreakpointInterceptor::arrayType),
brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstance),
brk("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;", BreakpointInterceptor::newArrayInstanceMulti),

Expand Down Expand Up @@ -1712,6 +1800,7 @@ private interface BreakpointHandler {
optionalBrk("jdk/internal/misc/Unsafe", "objectFieldOffset", "(Ljava/lang/Class;Ljava/lang/String;)J", BreakpointInterceptor::objectFieldOffsetByName),

brk("sun/misc/Unsafe", "allocateInstance", "(Ljava/lang/Class;)Ljava/lang/Object;", BreakpointInterceptor::allocateInstance),
brk("sun/misc/Unsafe", "objectFieldOffset", "(Ljava/lang/reflect/Field;)J", BreakpointInterceptor::accessFieldUnsafe),

optionalBrk("java/lang/invoke/MethodHandles$Lookup", "findStatic",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
Expand Down Expand Up @@ -1762,6 +1851,9 @@ private interface BreakpointHandler {
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflectSetter",
"(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;",
BreakpointInterceptor::unreflectField),
optionalBrk("java/lang/invoke/MethodHandles$Lookup", "unreflectVarHandle",
"(Ljava/lang/reflect/Field;)Ljava/lang/invoke/VarHandle;",
BreakpointInterceptor::unreflectField),
optionalBrk("java/lang/invoke/MethodHandleProxies", "asInterfaceInstance",
"(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;",
BreakpointInterceptor::asInterfaceInstance),
Expand Down Expand Up @@ -1810,6 +1902,24 @@ private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thre
brk("java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeMethod),
brk("sun/reflect/misc/MethodUtil", "invoke", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeMethod),
brk("java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeConstructor),
brk("java/lang/reflect/Field", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getBoolean", "(Ljava/lang/Object;)Z", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setBoolean", "(Ljava/lang/Object;Z)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getByte", "(Ljava/lang/Object;)B", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setByte", "(Ljava/lang/Object;B)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getShort", "(Ljava/lang/Object;)S", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setShort", "(Ljava/lang/Object;S)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getChar", "(Ljava/lang/Object;)C", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setChar", "(Ljava/lang/Object;C)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getInt", "(Ljava/lang/Object;)I", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setInt", "(Ljava/lang/Object;I)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getLong", "(Ljava/lang/Object;)J", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setLong", "(Ljava/lang/Object;J)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getFloat", "(Ljava/lang/Object;)F", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setFloat", "(Ljava/lang/Object;F)V", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "getDouble", "(Ljava/lang/Object;)D", BreakpointInterceptor::accessField),
brk("java/lang/reflect/Field", "setDouble", "(Ljava/lang/Object;D)V", BreakpointInterceptor::accessField),
};

private static final BreakpointSpecification[] CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ private static String reflectionNameToJNINameUnchecked(String reflectionName) {
return reflectionName.replace('.', '/');
}

public static String getArrayReflectionName(String componentReflectionName) {
if (!isValidReflectionName(componentReflectionName)) {
return componentReflectionName;
}
return "[" + (wrappingArrayDimension(componentReflectionName) > 0 ? componentReflectionName : typeNameToArrayElementType(componentReflectionName));
}

private static String arrayElementTypeToTypeName(String arrayElementType, int startIndex) {
char typeChar = arrayElementType.charAt(startIndex);
return switch (typeChar) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public final class AccessAdvisor {
// BytecodeDescriptor calls Class.forName
internalCallerFilter.addOrGetChildren("sun.invoke.util.BytecodeDescriptor", ConfigurationFilter.Inclusion.Include);
internalCallerFilter.addOrGetChildren("sun.launcher.**", ConfigurationFilter.Inclusion.Exclude);
// LoggingMXBeanAccess calls Class.forName
internalCallerFilter.addOrGetChildren("sun.management.ManagementFactoryHelper$LoggingMXBeanAccess", ConfigurationFilter.Inclusion.Exclude);
internalCallerFilter.addOrGetChildren("sun.misc.**", ConfigurationFilter.Inclusion.Exclude);
internalCallerFilter.addOrGetChildren("sun.net.**", ConfigurationFilter.Inclusion.Exclude);
// Uses constructor reflection on exceptions
Expand Down
Loading