fix(wallclock): add signal-origin guard to WallClockJvmti#569
Conversation
WallClockJvmti reused the SIGVTALRM delivery model without porting the signal-origin defence implemented in WallClockASGCT. Any foreign SIGVTALRM (debugger, in-process tgkill, external sigqueue) was accepted unconditionally and drove VM::requestStackTrace, producing spurious jdk.ExecutionSample events and a vector for fake profile injection. Three changes, all mirroring WallClockASGCT exactly: - sharedSignalHandler: add shouldProcessSignal gate before engine resolution; increment WALLCLOCK_SIGNAL_FOREIGN / _OWN accordingly - initialize: call OS::primeSignalOriginCheck() so the DDPROF_SIGNAL_ORIGIN_CHECK override is picked up for this engine - timerLoop: switch to sendSignalWithCookie so our own sends carry the cookie the new gate requires Tests: extend signalOrigin_ut.cpp with WallclockGuardContract cases (own / bare-kill / foreign-cookie); 19/19 pass on Linux/aarch64. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Ports the existing signal-origin validation used by the wallclock ASGCT engine over to the JVMTI-based wallclock engine to prevent foreign SIGVTALRM deliveries (e.g., debugger/in-process tgkill/external sigqueue) from triggering the JVMTI RequestStackTrace sampling path and producing spurious jdk.ExecutionSample events.
Changes:
- Add
OS::shouldProcessSignal(..., SI_QUEUE, SignalCookie::wallclock())gating toWallClockJvmti::sharedSignalHandler, withWALLCLOCK_SIGNAL_FOREIGN/_OWNcounters and foreign-signal forwarding. - Prime the signal-origin feature-flag cache in
WallClockJvmti::initialize()viaOS::primeSignalOriginCheck(). - Ensure wallclock JVMTI timer sends carry the wallclock cookie by switching to
OS::sendSignalWithCookie(...), and add Linux-only unit tests covering the wallclock guard contract.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
ddprof-lib/src/main/cpp/wallClock.cpp |
Adds signal-origin guard + priming to the JVMTI wallclock handler and switches JVMTI wallclock sampling sends to cookie-carrying delivery. |
ddprof-lib/src/test/cpp/signalOrigin_ut.cpp |
Adds Linux-only unit tests validating accept/reject behavior for the wallclock cookie + SI_QUEUE contract. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI Test ResultsRun: #26759293779 | Commit:
Status Overview
Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled Summary: Total: 32 | Passed: 32 | Failed: 0 Updated: 2026-06-01 14:09:22 UTC |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23acbdbdeb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
724f8cb to
d6d582a
Compare
What does this PR do?:
Port the signal-origin defence from
WallClockASGCTtoWallClockJvmti. Without this, any foreign SIGVTALRM (debugger, in-processtgkill, externalsigqueue) was accepted unconditionally by the JVMTI handler, drivingVM::requestStackTraceand producing spuriousjdk.ExecutionSampleevents.Three changes in
wallClock.cpp, all mirroringWallClockASGCTexactly:sharedSignalHandler: addOS::shouldProcessSignalgate before engine resolution; incrementWALLCLOCK_SIGNAL_FOREIGN/_OWNon reject/accept.initialize: callOS::primeSignalOriginCheck()so theDDPROF_SIGNAL_ORIGIN_CHECKenv-var override is picked up when JVMTI is the selected engine (the ASGCTinitializenever runs in that case).timerLoop: switch fromsendSignalToThreadtosendSignalWithCookieso our own sends carry the wallclock cookie the new gate requires.Motivation:
The original signal-origin design doc (
doc/plans/2026-04-21-signal-origin-validation.md, step 4) explicitly scoped the gate to bothWallClockASGCT::sharedSignalHandlerand the JVMTI variant. The JVMTI half was never implemented. Tracked in PROF-14753.Additional Notes:
OS::signalOriginCheckEnabled()returns false there, making the guard a no-op.DDPROF_SIGNAL_ORIGIN_CHECK=0disables the gate at runtime.WALLCLOCK_SIGNAL_OWNincrement; reversed forward/increment order). Both corrected to match ASGCT.How to test the change?:
Extended
ddprof-lib/src/test/cpp/signalOrigin_ut.cppwith threeWallclockGuardContractcases (Linux-only,#ifdef __linux__):WallclockGuardContract_OwnSignalAcceptedSI_QUEUE+SignalCookie::wallclock()→ acceptedWallclockGuardContract_BareKillRejectedSI_TKILL/SI_USER, no cookie → rejectedWallclockGuardContract_ForeignCookieRejectedSI_QUEUE+ foreign cookie → rejected19/19 pass on Linux/aarch64 glibc (Docker via
utils/run-docker-tests.sh):For Datadog employees:
@DataDog/security-design-and-guidance.