Skip to content

Protect Lookup::resolveMethod() with siglongjmp - #752

Merged
zhengyu123 merged 15 commits into
mainfrom
zgu/lookup_resolveMethod
Aug 27, 2026
Merged

Protect Lookup::resolveMethod() with siglongjmp#752
zhengyu123 merged 15 commits into
mainfrom
zgu/lookup_resolveMethod

Conversation

@zhengyu123

@zhengyu123 zhengyu123 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?:

Crash protection for method symbolication
Note: This mechanism cannot protect against faults originating from JNI or JVMTI calls, as the faulting PCs are outside the profiler’s address space. Since JNI and JVMTI are public APIs, such faults should generally not occur unless the APIs are misused.

Split resolveMethod() into a fast, deliberately-unprotected path (already-marked cache hit — provably non-faulting) and a new fillMethod() slow path, armed with its own sigsetjmp/JmpCtxScope landing pad before touching any VM metadata.

Added ResolveMethodState, an RAII holder for the intermediate JNI/JVMTI-owned pointers (_demangled, _class_name, _method_name, _method_signature, _line_number_table) acquired mid-resolution. state.release() frees whatever's still pending — on both the normal path and the siglongjmp recovery path — via jvmti->Deallocate().

Added a shared _unknown_method row (Lookup::unknownMethod()) that lives outside MethodMap, so the disarmed recovery branch can return a valid, already-marked row without allocating (a MethodMap::operator[] insert can throw std::bad_alloc, which must not happen with no landing pad installed).
fillJavaMethodInfo() now returns bool so a row is only marked/keyed when it was actually filled, preventing a permanently-stuck empty row when PushLocalFrame fails or the JVM isn't in JVMTI_PHASE_START/LIVE.
Added INJECT_CRASH_LIKELY() inside the protected window (compiled only under -PenableFaultInjection) to exercise the recovery path under test.

Line-number-table handling

SharedLineNumberTable no longer makes a private malloc'd copy of the JVMTI buffer — it now holds the buffer JVMTI itself returned and frees it via jvmti->Deallocate() instead of free(). (The previous copy-then-free() pairing could crash under -XX:NativeMemoryTracking, since HotSpot's Allocate/Deallocate route through NMT-tracked os::malloc/os::free, which add a header free() doesn't know about.)
Validation before trusting the buffer is now: a sanity bound on the reported size (MAX_LINE_NUMBER_TABLE_ENTRIES = 65535, the JVM spec's code_length cap) and a SafeAccess probe of the pointer — isReadableRange() for a non-zero size, isReadable() for the legitimate zero-size case Hotspot returns (calling isReadableRange(ptr, 0) would trip its assert(size > 0)).
LINE_NUMBER_TABLE_UNREADABLE is now actually incremented when the table is rejected.

Signal-depth bookkeeping

_signal_depth widened from uint8_t to int, and exitSignalScope()/getInSignalDepth() now assert() on an unmatched exit instead of silently clamping at zero — a pairing bug becomes visible in debug/test builds rather than self-healing invisibly. isInTrackedSignalContext() checks > 0 rather than != 0 so a (now theoretically possible) negative depth reads as "not in a signal context" rather than permanently pinning dlopen_hook to its synchronous refresh path.

Misc

faultInjection.h/.cpp: crashNow()'s declaration and definition are now consistently guarded behind __FAULT_INJECTION__ || DEBUG; the unused INJECT_CRASH_ALWAYS() macro (previously only defined in the disabled branch) was removed; Profiler's test-only forced-crash path now calls crashNow() instead of a local null-deref.
counters.h: added METHOD_RESOLUTION_DROPPED_TLS (dump thread couldn't allocate a ProfiledThread, expected to stay 0); trimmed the METHOD_RESOLVE_FAULT_RECOVERED comment (previously explained it as a non-additive subset of STACKWALK_LONGJMP_RECOVERED, tied to the old raw-Method*-only code path — worth re-checking whether that non-additive relationship still holds and re-documenting it, since the comment was dropped rather than updated).

Motivation:
Lookup::resolveMethod() resolves jmethodIDs into MethodInfo rows at JFR dump time by calling JVMTI (GetMethodDeclaringClass, GetClassSignature, GetMethodName, GetLineNumberTable). If the declaring class was unloaded between sample capture and dump, these calls can return stale/garbage pointers even with JVMTI_ERROR_NONE — production crash telemetry confirmed this on stock HotSpot, not just OpenJ9. This PR wraps that resolution path in a sigsetjmp/siglongjmp recovery window (building on the pattern already used for HotspotSupport::resolve(), #743) and fixes a related correctness/lifetime bug in how the JVMTI line-number-table buffer is validated and owned.

Additional Notes:

How to test the change?:

  • New lookup_resolveMethod_ut.cpp: exercises fillNativeMethodInfo()'s demangle-cut-cache-free lifecycle, and fillJavaMethodInfo()'s PushLocalFrame/PopLocalFrame balance (normal return and PushLocalFrame failure) via faked JNI/JVMTI, plus a line-number-table-survives-re-resolve-in-a-later-chunk case.

  • lineNumberTableCopy_ut.cpp rewritten to match the current hold-not-copy design: rejects an unmapped source, accepts a valid table and confirms it's held (not copied), accepts a zero-size table without tripping the isReadableRange assert, and rejects negative/oversized reported sizes. Ran directly (gtestDebug_lineNumberTableCopy_ut) — all 6 cases pass.

  • hotspotMethodId_ut.cpp updated: a rejected method id now resolves to the shared _unknown_method row instead of inserting into MethodMap.

  • signalSafety_ut.cpp: removed the now-invalid "saturates at zero" test, since double-exit is now an assert failure by design, not a saturating no-op.
    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 security review (run the dd:platform-security-review
    skill, or file a request via the PSEC review form).
    bewaire also runs automatically on every PR.

  • This PR doesn't touch any of that.

  • JIRA: PROF-15645

Unsure? Have a question? Request a review!

@dd-octo-sts

dd-octo-sts Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Scan-Build Report

User:runner@runnervmgx7h7
Working Directory:/home/runner/work/java-profiler/java-profiler/ddprof-lib/src/test/make
Command Line:make -j4 all
Clang Version:Ubuntu clang version 18.1.3 (1ubuntu1)
Date:Thu Aug 27 13:12:49 2026

Bug Summary

Bug TypeQuantityDisplay?
All Bugs1
Logic error
Dereference of null pointer1

Reports

Bug Group Bug Type ▾ File Function/Method Line Path Length
Logic errorDereference of null pointerfaultInjection.cppcrashNow242

@dd-octo-sts

dd-octo-sts Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #33075504486 | Commit: 30c3229 | Duration: 15m 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-08-27 13:30:30 UTC

@dd-octo-sts

dd-octo-sts Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

All 40 integration tests passed

📊 Dashboard · 👷 Pipeline · 📦 386f48ba

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Pipelines

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 386f48b | Docs | View more details | Give us feedback!

@zhengyu123
zhengyu123 marked this pull request as ready for review August 26, 2026 19:27
@zhengyu123
zhengyu123 requested a review from a team as a code owner August 26, 2026 19:27

@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: f87be0a19a

ℹ️ 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/flightRecorder.cpp Outdated

@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: c875dc9915

ℹ️ 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/flightRecorder.cpp

@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: fdd76c3424

ℹ️ 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/flightRecorder.cpp
@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

6 similar comments
@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

1 similar comment
@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

Comment thread ddprof-lib/src/main/cpp/flightRecorder.h Outdated
Comment thread ddprof-lib/src/main/cpp/flightRecorder.cpp Outdated
Comment thread ddprof-lib/src/main/cpp/flightRecorder.cpp
@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

2 similar comments
@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

@jbachorik

Copy link
Copy Markdown
Collaborator

A few nits and clarifying questions

Comment thread ddprof-lib/src/main/cpp/flightRecorder.cpp
Comment thread ddprof-lib/src/main/cpp/flightRecorder.cpp
@zhengyu123
zhengyu123 requested a review from jbachorik August 27, 2026 15:06

@jbachorik jbachorik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AFAICS, the change does what it says and I don't see any red flags.

Are we running it somewhere with fault injection? Just to make sure the recovery is working properly?

@zhengyu123

Copy link
Copy Markdown
Contributor Author

AFAICS, the change does what it says and I don't see any red flags.

Are we running it somewhere with fault injection? Just to make sure the recovery is working properly?

@jbachorik Yes, I injected a fault inLookup::fillMethod() to test the recovery.

@zhengyu123
zhengyu123 merged commit ba1683f into main Aug 27, 2026
107 of 108 checks passed
@zhengyu123
zhengyu123 deleted the zgu/lookup_resolveMethod branch August 27, 2026 18:00
@github-actions github-actions Bot added this to the 1.50.0 milestone Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants