Skip to content

fix(objectSampler): drop Deallocate on GetClassSignature error path#535

Open
jbachorik wants to merge 2 commits into
mainfrom
muse/fix-object-sampler-uaf
Open

fix(objectSampler): drop Deallocate on GetClassSignature error path#535
jbachorik wants to merge 2 commits into
mainfrom
muse/fix-object-sampler-uaf

Conversation

@jbachorik
Copy link
Copy Markdown
Collaborator

@jbachorik jbachorik commented May 18, 2026

What does this PR do?:

Removes the jvmti->Deallocate(class_name) call from the error branch of ObjectSampler::recordAllocation in ddprof-lib/src/main/cpp/objectSampler.cpp, and adds a TEST_F-based gtest fixture exercising the JVMTI mock for five error/success scenarios.

Motivation:

The error branch called Deallocate whenever class_name happened to be non-NULL after a failing GetClassSignature call. The JVMTI spec does not guarantee output buffers are populated on a non-JVMTI_ERROR_NONE return, so the pointer value is unspecified and passing it to Deallocate is unsafe — it crashed with SIGSEGV in jvmti_Deallocate reached via JvmtiExport::post_sampled_object_alloc (PROF-14626 / dd-crashsig-1b6db147ca4415c7, tracer 1.62.0*, Linux, JDK 25).

The success path Deallocate (still present at line 91) is unchanged: class_name is freed exactly once when GetClassSignature returns JVMTI_ERROR_NONE. The success branch's lookup-and-record flow is unaffected.

Additional Notes:

The test file uses a TEST_F fixture (ObjectSamplerDeallocateTest) so each test gets its own JVMTI function table and Deallocate counter, and TearDown restores the process-global ObjectSampler::_active flag — addressing review feedback from the muse chorus about shared static state, global counters, and singleton-flag leakage between tests.

How to test the change?:

Run the gtest suite:

./gradlew :ddprof-lib:gtestDebug_objectSampler_ut

Locally on macos-arm64: 15 tests pass (10 existing normalizeClassSignature cases + 5 new ObjectSamplerDeallocateTest cases — error+non-NULL sentinel, error+NULL name, success+NULL name, success+valid name (Deallocate called exactly once), and the !_active short-circuit).

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-14626

🤖 Generated with Claude Code

The error branch in ObjectSampler::recordAllocation called
jvmti->Deallocate(class_name) regardless of whether GetClassSignature
actually returned success. The JVMTI spec does not guarantee output
buffers are populated on a non-JVMTI_ERROR_NONE return; the pointer
value is unspecified, so passing it to Deallocate is unsafe and was
observed to crash with SIGSEGV (PROF-14626 / dd-crashsig-1b6db147ca4415c7).

Add a TEST_F-based regression suite that exercises recordAllocation
with a mock jvmtiEnv covering: error+non-NULL sentinel (the UAF),
error+NULL, success+NULL name, success+valid name, and the !_active
short-circuit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jbachorik jbachorik added the AI label May 18, 2026
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented May 18, 2026

CI Test Results

Run: #26095764878 | Commit: 33a54e7 | Duration: 32m 56s (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-05-19 12:37:43 UTC

@jbachorik jbachorik requested a review from Copilot May 19, 2026 11:59
@jbachorik
Copy link
Copy Markdown
Collaborator Author

@codex review

@jbachorik jbachorik marked this pull request as ready for review May 19, 2026 12:00
@jbachorik jbachorik requested a review from a team as a code owner May 19, 2026 12:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an unsafe JVMTI Deallocate call in ObjectSampler::recordAllocation by ensuring the profiler never attempts to free class_name when GetClassSignature returns an error, and adds gtest coverage to prevent regressions.

Changes:

  • Remove jvmti->Deallocate(class_name) from the GetClassSignature error path in ObjectSampler::recordAllocation.
  • Add a new TEST_F-based gtest fixture that validates Deallocate is called only on the success path across several error/success scenarios.
  • Add a small test-only friend accessor to control ObjectSampler::_active and call recordAllocation from unit tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
ddprof-lib/src/main/cpp/objectSampler.cpp Stops calling Deallocate on GetClassSignature failure to avoid freeing an unspecified pointer.
ddprof-lib/src/main/cpp/objectSampler.h Adds a friend accessor class used by unit tests to reach internals safely.
ddprof-lib/src/test/cpp/objectSampler_ut.cpp Introduces a JVMTI mock + TEST_F fixture covering Deallocate behavior for error/success paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// ---------------------------------------------------------------------------
// T-02: GetClassSignature succeeds with a valid class name.
// Deallocate IS called exactly once (on the success path).
// Note: after Deallocate, lookupClass returns -1 (empty class map) and the
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants