Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Merged
jbachorik merged 3 commits into
mainDataDog/java-profiler:mainfrom
muse/fix-object-sampler-uafDataDog/java-profiler:muse/fix-object-sampler-uafCopy head branch name to clipboard
May 20, 2026
Merged

fix(objectSampler): drop Deallocate on GetClassSignature error path#535
jbachorik merged 3 commits into
mainDataDog/java-profiler:mainfrom
muse/fix-object-sampler-uafDataDog/java-profiler:muse/fix-object-sampler-uafCopy head branch name to clipboard

Conversation

@jbachorik

@jbachorik jbachorik commented May 18, 2026

Copy link
Copy Markdown
Collaborator

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

dd-octo-sts Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #26116304322 | Commit: 8269eaf | Duration: 31m 55s (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 18:50:32 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

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

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.

Comment thread ddprof-lib/src/test/cpp/objectSampler_ut.cpp Outdated

@rkennke rkennke 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.

It seems weird that a JVM returns anything not-NULL in the failure case. But if that is what we observed then what else can we do? shrugs

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@jbachorik

Copy link
Copy Markdown
Collaborator Author

It seems weird that a JVM returns anything not-NULL in the failure case. But if that is what we observed then what else can we do? shrugs

@rkennke indeed, it is very unexpected. But we did collect crashes at that exact point and other explanations, like broken JVMTI env are not really holding, since this happens inside a JVMTI callback 🤷

@jbachorik jbachorik merged commit c5b8a1e into main May 20, 2026
98 checks passed
@jbachorik jbachorik deleted the muse/fix-object-sampler-uaf branch May 20, 2026 08:25
@github-actions github-actions Bot added this to the 1.43.0 milestone May 20, 2026
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.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.