fix(daemon): losing the runtime-directory creation race is not a failure on Windows - #2275
Merged
Merged
Conversation
…ure on Windows
Several processes first-starting together against a runtime directory
that does not exist yet -- test-windows-guards' section_cold_storm, or a
host that launches more than one MCP server on its very first run -- all
observe the final path component absent. One CreateDirectoryW wins; the
others get ERROR_ALREADY_EXISTS. win_private_directory_tree_secure()
treated that as a failure and recorded no validation detail, so every
loser exited with a bare
secure CLI coordination could not be created (endpoint)
The function called right after the walk, win_runtime_directory_secure(),
has always tolerated ERROR_ALREADY_EXISTS for the same directory, and the
POSIX walk tolerates EEXIST at the same point; only the Windows walk in
front of them did not.
Seen four times on 2026-09-21 on unrelated PRs (#1768 three attempts in
a row, #2140, #1999, #808). The racy lines date from July; the guard
began exercising them on 2026-09-03, when each guard section was given
its own empty CBM_RUNTIME_DIR, so the final component is now absent at
storm time on every run.
The walk now treats ERROR_ALREADY_EXISTS from its own CreateDirectoryW as
"the directory I wanted exists". Nothing is trusted because of that: an
ancestor still goes through win_directory_component_secure(), and the
final component through win_runtime_directory_secure(), which refuses a
non-directory or reparse point and enforces owner and DACL.
Every refusal on this path now names its component and its rule. The
walk reports the Windows error when it can neither create nor inspect a
component, and win_runtime_directory_secure() says whether the path
could not be created, cannot be inspected, exists but is not a directory,
or is a reparse point. One helper owns the wide-to-UTF-8 conversion for
these messages and the existing ancestor message now uses it too, so
src/daemon/ipc.c stays at its memory-core baseline.
Deterministic reproduction, no threads and no timing: a test seam fires
in the walk between "component observed absent" and CreateDirectoryW,
and the test plays the process that wins the creation.
daemon_ipc_windows_private_directory_survives_lost_creation_race
daemon_ipc_windows_private_directory_refuses_and_names_a_planted_file
Verification. macOS arm64 (ASan+UBSan): build clean, daemon_ipc 53 passed
(2 Windows-only skips), daemon_bootstrap 28 passed, lint-memory-core
unchanged (ipc.c stays at 146 raw sites). The two new tests are Windows-only
and have NOT been run locally: the Windows VM was down when this was written,
so the RED run on the seam-only tree and the GREEN run with this fix are
delegated to CI's windows-latest legs (test-windows shards, test-windows-guards)
by an explicit maintainer decision. If the guard suite's cold storm still
fails with this in place, the attribution above is wrong and this reverts.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This was referenced Sep 21, 2026
4 tasks
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.
Fixes the cold-start race that has been the only red on about ten contributor PRs today (
test-windows-guards→section_cold_storm→secure CLI coordination could not be created (endpoint)), and makes every refusal on that path name its reason so a bare(endpoint)can never happen again.⚠ Verification status — read first
The two new tests are Windows-only and have not been run locally: the Windows VM was down when this was written. By an explicit maintainer decision this PR is the first place the Windows code runs. What has been verified:
daemon_ipc53 passed (2 Windows-only skips);daemon_bootstrap28 passed; memory-core linter unchanged (ipc.cstays at its 146 raw sites, the new message helper is shared with the existing ancestor path).EEXISTat the same point.What CI must show before this merges:
test-windowsshards green includingdaemon_ipc_windows_private_directory_survives_lost_creation_raceand…_refuses_and_names_a_planted_file, andtest-windows-guardsgreen. If the guard's cold storm still fails with this in place, the attribution is wrong and this PR is withdrawn, not patched.The defect
Several processes first-starting together against a runtime directory that does not exist yet — the guard's cold storm, or a host launching more than one MCP server on its very first run — all observe the final path component absent. One
CreateDirectoryWwins; the others getERROR_ALREADY_EXISTS.win_private_directory_tree_secure()treated that as a failure and recorded no validation detail, so every loser exited with a bareThe function called right after the walk,
win_runtime_directory_secure(), has always toleratedERROR_ALREADY_EXISTSfor the same directory, and the POSIX walk toleratesEEXISTat the same point. Only the Windows walk in front of them did not.Seen four times on 21 September on unrelated PRs (#1768 three attempts in a row, #2140, #1999, #808). The racy lines date from July; the guard began exercising them on 3 September, when each guard section was given its own empty
CBM_RUNTIME_DIR, so the final component is now absent at storm time on every run.The fix
ERROR_ALREADY_EXISTSfrom its ownCreateDirectoryWas "the directory I wanted exists". Nothing is trusted because of that: an ancestor still goes throughwin_directory_component_secure(), and the final component throughwin_runtime_directory_secure(), which refuses a non-directory or reparse point and enforces owner and DACL.win_runtime_directory_secure()says whether the path could not be created, cannot be inspected, exists but is not a directory, or is a reparse point.Deterministic tests, no threads, no timing
A test seam fires in the walk between "component observed absent" and
CreateDirectoryW; the test plays the process that wins the creation:daemon_ipc_windows_private_directory_survives_lost_creation_race— the racer creates the directory; the walk must succeed and the directory must end up owner-only.daemon_ipc_windows_private_directory_refuses_and_names_a_planted_file— the racer plants a file; the walk must refuse and the detail must name the path and "not a directory".Both assert that the seam actually fired and the racer actually won, so neither can pass without exercising the race.