Self-standing reproducer for a memory corruption on OpenJ9 (J9) x86-64 Linux with the Datadog java-profiler CPU sampler running. The corruption is caused by the Linux kernel writing a signal frame over the J9 OSR (On-Stack Replacement) buffer, not by any agent code.
- On OpenJ9 x86-64, the JIT OSR transition temporarily runs with the hardware stack pointer (rsp) pointing into a heap-allocated OSR block (osrJittedFrameCopy), not the thread native stack. (runtime/codert_vm/decomp.cpp:2047-2087, runtime/oti/xhelpers.m4:313-315)
- The Linux kernel writes the signal frame below the interrupted rsp unless the handler uses SA_ONSTACK (arch/x86/kernel/signal.c:99-121). The frame includes the xsave area whose trailer is FP_XSTATE_MAGIC2 (0x46505845 = bytes "EXPF") (arch/x86/include/uapi/asm/sigcontext.h:21-35).
- The profiler CPU sampler installs SIGPROF handlers without SA_ONSTACK (ddprof-lib/src/main/cpp/os_linux.cpp:306-318) and on OpenJ9 uses the SIGPROF+ASGCT path (profiler.cpp:1277-1292).
- A SIGPROF delivered while rsp is in the OSR block makes the kernel write the signal frame over the OSR buffer (J9OSRBuffer.numberOfFrames, J9OSRFrame.numberOfLocals), corrupting it.
- heap_sigframe.c — minimal C reproducer of the kernel mechanism: switches rsp to a heap buffer, delivers SIGPROF (no SA_ONSTACK), and finds FP_XSTATE_MAGIC2 ("EXPF") written into the heap buffer below rsp. Deterministic.
- HCRRepro.java — J9 reproducer: a Java agent that repeatedly retransforms its own class (forcing HCR/OSR) while a hot worker thread runs, under the profiler high-frequency SIGPROF.
- osr-detection.patch — minimal profiler change: in CTimer::signalHandler, when a SIGPROF signal frame overlaps the OSR heap block, abort with a clear message. Turns the silent corruption into a deterministic failure.
- run-repro.sh — builds the agent jar and runs the reproducer.
Requires: x86-64 Linux, an OpenJ9 JDK 8 (Semeru), and a profiler jar built with osr-detection.patch applied.
# 1. build the profiler with the detection patch
./gradlew buildDebug -Pskip-tests
# 2. compile the Java reproducer
javac -cp ddprof-<version>-debug.jar -d classes HCRRepro.java
# 3. run (deterministic abort)
./run-repro.sh ddprof-<version>-debug.jar /path/to/openj9-jdk8/bin
# expected output:
# === DETERMINISTIC REPRO: SIGPROF signal frame overlaps J9 OSR buffer ===
# JVMDUMP042W Abort signal received while running on Java stack.
# control (JVMTI sampler, no SIGPROF) — runs clean:
java -Ddd.profiling.ddprof.j9.sampler=jvmti -javaagent:osr-hcr-agent.jar \
-cp classes HCRRepro 100 20000gcc -O0 -g -o heap_sigframe heap_sigframe.c && ./heap_sigframe
# found FP_XSTATE_MAGIC2 at offset=... distance_below_top=...With the patch, each SIGPROF in the OSR window logs: rsp=0x221e60 rip= expf=1 expf_off=3944 osr_hit=1 — rsp in the OSR heap block, rip in the JIT code cache (JIT running on the OSR heap stack), and the signal frame (EXPF) present 3944 bytes below rsp, overlapping the OSR buffer.