Skip to content

fix(wallclock): add signal-origin guard to WallClockJvmti#569

Merged
jbachorik merged 2 commits into
mainfrom
jb/prof-14753-wallclock-jvmti-signal-origin
Jun 2, 2026
Merged

fix(wallclock): add signal-origin guard to WallClockJvmti#569
jbachorik merged 2 commits into
mainfrom
jb/prof-14753-wallclock-jvmti-signal-origin

Conversation

@jbachorik

@jbachorik jbachorik commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?:
Port the signal-origin defence from WallClockASGCT to WallClockJvmti. Without this, any foreign SIGVTALRM (debugger, in-process tgkill, external sigqueue) was accepted unconditionally by the JVMTI handler, driving VM::requestStackTrace and producing spurious jdk.ExecutionSample events.

Three changes in wallClock.cpp, all mirroring WallClockASGCT exactly:

  • sharedSignalHandler: add OS::shouldProcessSignal gate before engine resolution; increment WALLCLOCK_SIGNAL_FOREIGN/_OWN on reject/accept.
  • initialize: call OS::primeSignalOriginCheck() so the DDPROF_SIGNAL_ORIGIN_CHECK env-var override is picked up when JVMTI is the selected engine (the ASGCT initialize never runs in that case).
  • timerLoop: switch from sendSignalToThread to sendSignalWithCookie so 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 both WallClockASGCT::sharedSignalHandler and the JVMTI variant. The JVMTI half was never implemented. Tracked in PROF-14753.

Additional Notes:

  • macOS is unaffected: OS::signalOriginCheckEnabled() returns false there, making the guard a no-op.
  • Kill-switch: DDPROF_SIGNAL_ORIGIN_CHECK=0 disables the gate at runtime.
  • The ticket's proposed snippet had two inaccuracies (missing WALLCLOCK_SIGNAL_OWN increment; reversed forward/increment order). Both corrected to match ASGCT.
  • Stale line numbers in the ticket (L323/L299/L250) — actual locations in current code are L311/L284/L234.

How to test the change?:
Extended ddprof-lib/src/test/cpp/signalOrigin_ut.cpp with three WallclockGuardContract cases (Linux-only, #ifdef __linux__):

Test Assertion
WallclockGuardContract_OwnSignalAccepted SI_QUEUE + SignalCookie::wallclock() → accepted
WallclockGuardContract_BareKillRejected SI_TKILL/SI_USER, no cookie → rejected
WallclockGuardContract_ForeignCookieRejected SI_QUEUE + foreign cookie → rejected

19/19 pass on Linux/aarch64 glibc (Docker via utils/run-docker-tests.sh):

[==========] Running 19 tests from 1 test suite.
[ RUN      ] SignalOriginTest.WallclockGuardContract_OwnSignalAccepted
[       OK ] SignalOriginTest.WallclockGuardContract_OwnSignalAccepted (0 ms)
[ RUN      ] SignalOriginTest.WallclockGuardContract_BareKillRejected
[       OK ] SignalOriginTest.WallclockGuardContract_BareKillRejected (0 ms)
[ RUN      ] SignalOriginTest.WallclockGuardContract_ForeignCookieRejected
[       OK ] SignalOriginTest.WallclockGuardContract_ForeignCookieRejected (0 ms)
[  PASSED  ] 19 tests.

For Datadog employees:

  • If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from @DataDog/security-design-and-guidance.
  • This PR doesn't touch any of that.
  • JIRA: PROF-14753

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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 to WallClockJvmti::sharedSignalHandler, with WALLCLOCK_SIGNAL_FOREIGN/_OWN counters and foreign-signal forwarding.
  • Prime the signal-origin feature-flag cache in WallClockJvmti::initialize() via OS::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.

@dd-octo-sts

dd-octo-sts Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #26759293779 | Commit: 1782565 | Duration: 12m 25s (longest job)

All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - - -
8-ibm - - -
8-j9 - -
8-librca - -
8-orcl - - -
11 - - -
11-j9 - -
11-librca - -
17 - -
17-graal - -
17-j9 - -
17-librca - -
21 - -
21-graal - -
21-librca - -
25 - -
25-graal - -
25-librca - -

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-06-01 14:09:22 UTC

@jbachorik jbachorik marked this pull request as ready for review June 1, 2026 13:19
@jbachorik jbachorik requested a review from a team as a code owner June 1, 2026 13:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread ddprof-lib/src/main/cpp/wallClock.cpp
@jbachorik jbachorik force-pushed the jb/prof-14753-wallclock-jvmti-signal-origin branch from 724f8cb to d6d582a Compare June 1, 2026 13:53

@kaahos kaahos left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@jbachorik jbachorik merged commit 8c433d5 into main Jun 2, 2026
103 checks passed
@jbachorik jbachorik deleted the jb/prof-14753-wallclock-jvmti-signal-origin branch June 2, 2026 05:36
@github-actions github-actions Bot added this to the 1.45.0 milestone Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants