[release/11.0] [CreateDump] Use _exit after createdump launch failures - #133928
Merged
Merged
Conversation
Fixes #133733 Use `_exit(EXIT_FAILURE)` instead of `exit(-1)` when the forked `createdump` launcher child fails before completing `execve`. On Unix, CoreCLR forks a temporary child and expects it to replace itself with `createdump`. If the parent/child synchronization handshake fails or `execve(createdump)` fails, the child is still a forked copy of the managed runtime. Calling `exit()` in that child runs inherited process destructors. In particular, the PAL process-shutdown destructor invokes the CoreCLR shutdown callback, which cleans up the debugger transport and diagnostic server. Because the child shares the parent's filesystem namespace, that cleanup unlinks paths belonging to the still-running parent: ```text /tmp/clr-debug-pipe-<pid>-<key>-in /tmp/clr-debug-pipe-<pid>-<key>-out /tmp/dotnet-diagnostic-<pid>-<key>-socket ``` The parent runtime remains alive and retains its diagnostic listener file descriptor, but new diagnostic clients cannot connect after the Unix-domain socket pathname has been removed. ## Cleanup behavior before this change On a child failure: ```text forked child | | handshake or execve failure v exit(-1) | | runs inherited destructors v PAL shutdown callback | +-- closes the child's inherited descriptor references +-- runs debugger transport cleanup +-- runs diagnostic server shutdown +-- unlinks debugger pipe and diagnostic socket paths ``` The kernel eventually closed the child's file descriptors when the child terminated, but `exit()` first ran userspace cleanup that belonged to the parent runtime. Unlinking the shared endpoint paths affected the parent even though closing the child's descriptor references alone would not have affected it. ## Cleanup behavior after this change On a child failure: ```text forked child | | handshake or execve failure v _exit(EXIT_FAILURE) | +-- kernel closes the child's file descriptors +-- kernel releases the child's mappings and other process resources +-- parent observes EOF on the child stderr pipe +-- parent reaps the child with waitpid() | +-- does not run inherited destructors +-- does not invoke the PAL shutdown callback +-- does not unlink the parent's diagnostic endpoint paths ``` The child still closes all of its file descriptor references as part of kernel process termination. The difference is that it no longer performs inherited userspace teardown against copied runtime state. On a successful `execve`, the existing `SOCK_CLOEXEC` and `FD_CLOEXEC` flags continue to close inherited diagnostic descriptors in the new `createdump` process. This change affects only failure paths before a successful exec. Both forked-child failure paths are updated: 1. Failure to read the one-byte synchronization signal from the parent. 2. Failure to execute the external `createdump` binary. ## Validation The issue was reproduced by running a target with its colocated `createdump` set to mode `0644`. Before the change: 1. `dotnet-dump collect` reached the target runtime. 2. `execve(createdump)` failed with `EACCES`. 3. The target process remained alive. 4. The target's default diagnostic socket disappeared. After the change: 1. Two consecutive dump requests failed with the expected execute-permission error. 2. The target process remained alive after both requests. 3. The default diagnostic socket remained present after both requests. 4. The second request successfully connected to the same runtime, confirming that the listener remained reachable. 5. After restoring execute permission, a subsequent minidump completed successfully. 6. The diagnostic socket remained present after successful collection. CoreCLR Release was rebuilt successfully with the change. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
github-actions
Bot
requested a review
from MichalStrehovsky
as a code owner
September 15, 2026 03:40
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Member
|
Hi, the code complete date for RC2 is Monday 21st September. Make sure to merge this PR on that date at the latest, or it won't make it into that release. As a reminder, if this is a product change, you also need Tactics approval before merging this PR (test-only or infra-only changes don't require Tactics approval). |
steveisok
approved these changes
Sep 18, 2026
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.
Backport of #133742 to release/11.0
/cc @steveisok @mdh1418
Customer Impact
Customers collecting dumps via
dotnet-dumpwith a companion createdump missing exec permissions will trigger cleanup that normally runs during shutdown (e.g. closing diagnostic ports) even though the app is still running.The expected behavior is that cleanup logic does not run when the forked child process fails to
execveinto createdump, and just exits without closing diagnostic ports for example.Regression
Testing
Before the fix, ran a live CoreCLR target with it's colocated
createdumpset to mode 0644 .The first
dotnet-dump collectreached the runtime and failed atexecvewithEACCES.The target remained alive, but its default diagnostic socket disappeared, reproducing the issue.
After rebuilding with the fix (
_exit(EXIT_FAILURE)), two consecutive collections both reached the runtime and returned the expected permission error.After each failure, the target remained alive and the same diagnostic socket remained present.
After restoring execute permission, a subsequent minidump completed successfully.
Risk
Low.
Successful
execve/execvbehavior is unchanged.Parent process behavior is unchanged: it reads the error pipe, calls
waitpid, and treats any nonzero child exit status as failure._exitis the standard operation for abandoning a forked child process beforeexec, especially in a multithreaded process. It prevents inheritedatexithandlers and destructors from operating on copied parent-runtime state.Kernel cleanup still closes all child descriptors, releases mappings, signals pipe EOF, records the exit status, and permits
waitpidto reap the child process.IMPORTANT: If this backport is for a servicing release, please verify that:
release/X.0-staging, notrelease/X.0.release/X.0(no-stagingsuffix).Package authoring no longer needed in .NET 9
IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.