Don't dlclose the legacy DAC on non-Windows - #1499
Merged
Max Charlamb (max-charlamb) merged 1 commit intoJul 21, 2026
Merged
Conversation
Juan Hoyos (hoyosjs)
requested review from
Lee Culver (leculver) and
Max Charlamb (max-charlamb)
July 21, 2026 00:13
Noah Falk (noahfalk)
approved these changes
Jul 21, 2026
Lee Culver (leculver)
approved these changes
Jul 21, 2026
Max Charlamb (max-charlamb)
approved these changes
Jul 21, 2026
Pedro Sakuma Travi (pedrosakuma)
added a commit
to pedrosakuma/dotnet-diagnostics
that referenced
this pull request
Jul 24, 2026
* chore: bump Microsoft.Diagnostics.Runtime (ClrMD) 3.1.512801 -> 4.0.732401 Upgrades to the latest published ClrMD release so we can pick up a fix faster once one ships. microsoft/clrmd#1499 ("Don't dlclose the legacy DAC on non-Windows", merged 2026-07-21) plausibly fixes our Linux CI host-crash flake (issue #147 / dotnet/runtime#128525): its root cause (TLS destructor left dangling after dlclose-ing the DAC, crashing an unrelated thread in __nptl_deallocate_tsd on exit) matches our own findings (unmanaged runtime thread, deterministic low-offset fault in an unmapped thread-stack region). No published NuGet package includes the fix yet (latest is 4.0.732401, published 2026-06-25, before the merge), so this is purely a version bump to the current major to shorten the upgrade path later — no functional change expected, and the flake still reproduces locally on this version as expected. Verified: dotnet build DotnetDiagnostics.slnx -c Release (0 errors) and dotnet test tests/DotnetDiagnostics.Core.Tests (825 passed, 1 skipped, same known host-crash flake, no real failures). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: tolerate ClrMD 4.0 taking the direct ThreadPool path on Windows ClrMD 4.0.732401's runtime.ThreadPool now succeeds during live attach on Windows (previously null with 3.1.512801, forcing the lightweight thread-snapshot fallback). ThreadSnapshot_InspectLive_CapturesThreadPoolSnapshot hardcoded the fallback-only note ("heap-wide walks"); on Windows CI the direct path now runs instead, which still avoids a heap-wide local-queue owner walk during live suspended capture and says so via a different note ("heap-wide scans", see ClrMdThreadSnapshotInspector.cs CaptureLocalQueues). Widen the assertion to accept either note — which code path ClrMD takes is a runtime/ClrMD-version implementation detail, not part of this test's contract. No production code changed; both paths were already correct. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intermittent (~15-20%) SIGSEGV in the test host on Linux, surfacing as
Test host process crashed/ aborted runs rather than test failures. Faulting thread:The DAC embeds the PAL. ClrMD brings the PAL up by calling the DAC's exported
DllMain, which initializes TLS, registering a pthread key with a destructor in the DAC. That destructor pointer lives in glibc's per-thread key table, not in the DAC.RefCountedFreeLibrary.Releasedlcloses the DAC onDataTargetdispose, but the pthread key is never deleted. Any host thread that entered the DAC's PAL and later exits jumps into the now-unmapped destructor from__nptl_deallocate_tsd.The existing suppression only covered inspecting the current process, so dump-file targets still unloaded the DAC.
Why not tear the PAL down instead of leaking?
There is no in-process, reversible PAL teardown to call before
dlclose:DllMain(DLL_PROCESS_DETACH)does not touch the PAL - it only destroys the dac mutex (daccess.cppDllMain2). It never deletes the pthread key.pthread_key_deleteonly runs viaTLSCleanup, which is reachable only on PAL init-failure unwind or the process-terminate path.PAL_TerminateEx, ends inexit(exitCode)- it shuts down and exits the whole process. Its sole caller iscreatedump, which is exiting anyway and passes an exit code. A long-lived host that uses ClrMD cannot call it.Fix
Never free the DAC on non-Windows so the destructor remains mapped. Effectively leaks one mapping per unique DAC path.
Validation
Root cause proven by logging the DAC base +
addr2lineon the fault offset ->InternalEndCurrentThreadWrapper.