Repository navigation
Replace non-versioned files instead of rewriting them in place (fixes "Killed: 9" on macOS) - #743
akirayamamoto wants to merge 4 commits into
Conversation
Rewriting dotnet in place while another dotnet process is running gets the next launch killed on macOS, because the kernel checks it against the old cached code signature. Copy to a temporary name and rename it over the target instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@dotnet-policy-service agree company="Octopus Deploy" |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
/azp run |
|
Commenter does not have sufficient privileges for PR 743 in repo dotnet/install-scripts |
|
Hi @ellahathaway and @MichaelSimons, would one of you have a moment to look at this? It fixes the "Killed: 9" crash on macOS when you install into a folder that already has The Windows failure looks like the dotnet.exe file lock flake during test cleanup. I opened #744 (retry test cleanup while Windows still locks dotnet.exe) for that. I can't trigger Thanks! |
@akirayamamoto , thanks for your contribution. @dotnet/install-scripts-maintainers is the team to ping for approval or other questions. @ellahathaway and myself, are focusing in different areas at the moment. |
Why
On macOS, installing an SDK or runtime into a folder that already has one can leave
dotnetunusable: every launch is killed withKilled: 9(exit code 137, crash reportSIGKILL (Code Signature Invalid)).codesign --verifyreports the file as valid, and copying it to a new file fixes it.#232 reported this root cause and was closed with the
--skip-non-versioned-filesworkaround. A comment there suggested it stopped happening on macOS 13 and later, but it still reproduces on macOS 26 (see Validation). #622 and #662 look like the same symptom but were attributed to an incompatibledotnetand to running out of memory.Root cause
copy_files_or_dirs_from_listcopies each non-versioned file withcpover the existing one.cpwrites into the existing file rather than replacing it.macOS attaches a binary's code signature to the file while any process has it loaded. If
dotnetis rewritten in place while anotherdotnetprocess is still running (an MSBuild node, the Roslyn compiler server, an IDE language server), the next launch is checked against the old signature and the kernel kills it. With nothing else running, the overwrite is harmless, which is why it only fails some of the time.Apple's guidance is to write the new file separately and rename it into place: Updating Mac software.
Approach
When the target is an existing regular file, copy to a temporary name in the same folder and
mvit over the target. A rename gives a new file atomically, so a running process keeps the old one and new launches get a fresh signature check. Folders, symlinks and new files are copied as before, and--skip-non-versioned-filesis unchanged.This also avoids
Text file busyon Linux when overwriting a binary that is running.dotnet-install.ps1is unchanged: it only installs Windows builds, and this signature caching is macOS-specific.Validation
OverwrittenNonVersionedFileIsReplacedNotRewrittenInPlace: holds the existingdotnetopen during extraction, like a running process, and checks that handle still reads the old content. It fails without the fix.BashTarExtractionTestspass via./build.sh --test, filtered to that class.dotnet app.dllfrom that folder running, install the .NET 8 runtime into the same folder (--runtime dotnet --channel 8.0), then launchdotnetagain:dotnet --list-runtimesmainWithout a running process, the in-place overwrite didn't fail in my runs.
🤖 Generated with Claude Code