Skip to content

[GR-57294] Track Class#getSigners in agent by proxying native call. #10354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 7, 2025
Merged
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
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -317,9 +317,13 @@ private static boolean getSigners(JNIEnvironment jni, JNIObjectHandle thread, Br
return handleGetClasses(jni, thread, bp, state);
}

private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, AbstractBreakpoint<?> bp, InterceptedState state) {
JNIObjectHandle self = getReceiver(thread);
return handleGetClassesWithReceiver(jni, self, bp, state);
}

private static boolean handleGetClassesWithReceiver(JNIEnvironment jni, JNIObjectHandle self, AbstractBreakpoint<?> bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
traceReflectBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull());
return true;
}
@@ -366,10 +370,10 @@ private interface AllocateInstanceFunctionPointer extends CFunctionPointer {
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
static long nativeAllocateInstance(JNIEnvironment jni, JNIObjectHandle self, JNIObjectHandle clazz) {
VMError.guarantee(NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed != null &&
NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed.replacedFunction.isNonNull(), "incompletely installed");
NativeBreakpoint breakpoint = NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed;
VMError.guarantee(breakpoint != null && breakpoint.replacedFunction.isNonNull(), "incompletely installed");

AllocateInstanceFunctionPointer original = (AllocateInstanceFunctionPointer) NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed.replacedFunction;
AllocateInstanceFunctionPointer original = (AllocateInstanceFunctionPointer) breakpoint.replacedFunction;
long result = original.invoke(jni, self, clazz);
if (!Support.isInitialized()) { // in case of a (very) late call
return result;
@@ -392,6 +396,36 @@ private static void traceAllocateInstance(JNIEnvironment jni, JNIObjectHandle cl
}
}

private static final CEntryPointLiteral<AllocateInstanceFunctionPointer> nativeGetSigners = CEntryPointLiteral.create(
BreakpointInterceptor.class, "nativeGetSigners", JNIEnvironment.class, JNIObjectHandle.class);
private static final NativeBreakpointSpecification NATIVE_GET_SIGNERS_BREAKPOINT_SPEC = new NativeBreakpointSpecification(
"java/lang/Class", "getSigners", "()[Ljava/lang/Object;", nativeGetSigners);

private interface GetSignersFunctionPointer extends CFunctionPointer {
@InvokeCFunctionPointer
JNIObjectHandle invoke(JNIEnvironment jni, JNIObjectHandle self);
}

/**
* Before Java 24, {@code java/lang/Class#getSigners} was declared as a native method, so we use
* a native breakpoint to trace it. Either this breakpoint or the Java breakpoint will be set
* (but not both).
*/
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
static JNIObjectHandle nativeGetSigners(JNIEnvironment jni, JNIObjectHandle self) {
NativeBreakpoint breakpoint = NATIVE_GET_SIGNERS_BREAKPOINT_SPEC.installed;
VMError.guarantee(breakpoint != null && breakpoint.replacedFunction.isNonNull(), "incompletely installed");
GetSignersFunctionPointer original = (GetSignersFunctionPointer) breakpoint.replacedFunction;
JNIObjectHandle result = original.invoke(jni, self);
if (!Support.isInitialized()) {
return result;
}
InterceptedState state = interceptedStateSupplier.get();
handleGetClassesWithReceiver(jni, self, breakpoint, state);
return result;
}

private static boolean objectFieldOffsetByName(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
@@ -1326,17 +1360,13 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
BreakpointInterceptor.experimentalClassDefineSupport = exptlClassDefineSupport;
BreakpointInterceptor.experimentalUnsafeAllocationSupport = exptlUnsafeAllocationSupport;
BreakpointInterceptor.trackReflectionMetadata = trackReflectionData;
BreakpointInterceptor.boundNativeMethods = new HashMap<>();

JvmtiCapabilities capabilities = UnmanagedMemory.calloc(SizeOf.get(JvmtiCapabilities.class));
check(jvmti.getFunctions().GetCapabilities().invoke(jvmti, capabilities));
capabilities.setCanGenerateBreakpointEvents(1);
capabilities.setCanAccessLocalVariables(1);

if (exptlUnsafeAllocationSupport) {
capabilities.setCanGenerateNativeMethodBindEvents(1);
callbacks.setNativeMethodBind(onNativeMethodBindLiteral.getFunctionPointer());
BreakpointInterceptor.boundNativeMethods = new HashMap<>();
}
capabilities.setCanGenerateNativeMethodBindEvents(1);

if (exptlClassLoaderSupport) {
capabilities.setCanGetBytecodes(1);
@@ -1348,8 +1378,10 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
}
check(jvmti.getFunctions().AddCapabilities().invoke(jvmti, capabilities));
UnmanagedMemory.free(capabilities);
check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, nullHandle()));

callbacks.setBreakpoint(onBreakpointLiteral.getFunctionPointer());
callbacks.setNativeMethodBind(onNativeMethodBindLiteral.getFunctionPointer());

if (exptlClassDefineSupport) {
callbacks.setClassFileLoadHook(onClassFileLoadHookLiteral.getFunctionPointer());
@@ -1359,9 +1391,6 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
callbacks.setClassPrepare(onClassPrepareLiteral.getFunctionPointer());
}

if (exptlUnsafeAllocationSupport) {
Support.check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, nullHandle()));
}
}

public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) {
@@ -1412,9 +1441,7 @@ public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) {
}
installedBreakpoints = breakpoints;

if (experimentalUnsafeAllocationSupport) {
setupNativeBreakpoints(jni, lastClass, lastClassName);
}
setupNativeBreakpoints(jni, lastClass, lastClassName);

if (experimentalClassDefineSupport) {
setupClassLoadEvent(jvmti, jni);
@@ -1431,8 +1458,12 @@ private static void setupNativeBreakpoints(JNIEnvironment jni, JNIObjectHandle p
String lastClassName = previousClassName;
nativeBreakpointsInitLock.lock();
try {
nativeBreakpoints = new HashMap<>(NATIVE_BREAKPOINT_SPECIFICATIONS.length);
for (NativeBreakpointSpecification br : NATIVE_BREAKPOINT_SPECIFICATIONS) {
List<NativeBreakpointSpecification> nativeBreakpointsList = new ArrayList<>(Arrays.asList(NATIVE_BREAKPOINT_SPECIFICATIONS));
if (experimentalUnsafeAllocationSupport) {
nativeBreakpointsList.addAll(Arrays.asList(EXPERIMENTAL_UNSAFE_ALLOCATION_NATIVE_BREAKPOINT_SPECIFICATIONS));
}
nativeBreakpoints = new HashMap<>(nativeBreakpointsList.size());
for (NativeBreakpointSpecification br : nativeBreakpointsList) {
JNIObjectHandle clazz;
if (lastClassName != null && lastClassName.equals(br.className)) {
clazz = lastClass;
@@ -1446,7 +1477,8 @@ private static void setupNativeBreakpoints(JNIEnvironment jni, JNIObjectHandle p
NativeBreakpoint bp = new NativeBreakpoint(br, clazz, method);
nativeBreakpoints.put(method.rawValue(), bp);
Long original = boundNativeMethods.get(method.rawValue());
if (original != null) { // already bound, replace
// only bind a native breakpoint if method was bound as a native method
if (original != null) {
bindNativeBreakpoint(jni, bp, WordFactory.pointer(original), nullPointer());
}
}
@@ -1710,7 +1742,7 @@ private interface BreakpointHandler {
optionalBrk("java/lang/Class", "getNestMembers", "()[Ljava/lang/Class;",
BreakpointInterceptor::getNestMembers),
optionalBrk("java/lang/Class", "getSigners", "()[Ljava/lang/Object;",
BreakpointInterceptor::getSigners)
BreakpointInterceptor::getSigners),
};

private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {
@@ -1724,6 +1756,10 @@ private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thre
"(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::loadClass);

private static final NativeBreakpointSpecification[] NATIVE_BREAKPOINT_SPECIFICATIONS = {
NATIVE_GET_SIGNERS_BREAKPOINT_SPEC,
};

private static final NativeBreakpointSpecification[] EXPERIMENTAL_UNSAFE_ALLOCATION_NATIVE_BREAKPOINT_SPECIFICATIONS = {
NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC
};