Skip to content

fix(storage): avoid Windows asynchronous-handle read crashes - #912

Merged
apotema merged 1 commit into
mainfrom
codex/972-windows-storage
Sep 30, 2026
Merged

apotema merged 1 commit into
mainfrom
codex/972-windows-storage

Conversation

@apotema

@apotema apotema commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Windows save reads crash because Zig 0.16 opens no-follow files with asynchronous NT handles but marks them synchronous. Set the returned file's nonblocking flag so reads use Zig's APC/wait path, preserving no-follow and regular-file checks. This fixes the six save_storage crashes reported in moca-tecnologia/flying-platform-labelle#972.

Add a real-store regression for 128 KiB+17 payloads, three reopen cycles, inclusive limits and TooLarge. Make the existing purge fixtures portable using File.setTimestamps and removing the placeholder left by a failed Windows symlink creation.

Validation on Windows, Zig 0.16.0:

  • Unmodified Flying Platform main f468887: 321 passed, 1 skipped, 6 crashed in save_storage.
  • Same generated build with only this storage source substituted: 327 passed, 1 skipped, no crashes; complete generated test build exited 0.
  • Engine zig build test-storage -j2 --summary all: 17 passed, 2 skipped (Windows symlink privilege/FIFO).
  • Read-only helper review: no actionable findings.

Flying Platform still pins engine 3.4.1. Its dependency update must follow a released engine containing this fix; no local path override is committed.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T14:44:09.477137Z 2c4b66d PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 3653a83c-1a81-4ef9-9ce5-4fbaadf4596e

📥 Commits

Reviewing files that changed from the base of the PR and between 6ce947c and 2c4b66d.

📒 Files selected for processing (2)
  • src/storage/files.zig
  • test/storage_test.zig

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

Windows file opening now sets the nonblocking flag for regular files. Storage tests add coverage for exact-limit reads across reopen cycles and adjust staging fixtures for Windows behavior.

Changes

Storage file handling

Layer / File(s) Summary
File opening and exact-limit reads
src/storage/files.zig, test/storage_test.zig
Windows sets the opened file’s nonblocking flag after the regular-file check. A test verifies exact reads after three reopen cycles and expects TooLarge when the limit is one byte smaller.
Staging test fixtures
test/storage_test.zig
Staging tests set modification times through opened files. Symlink setup removes a possible Windows placeholder on failure, and the staging scan skips the known foreign symlink when created.

Priority: ⬆️ High

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 2c4b6

The change targets Windows save-read crashes, and the PR reports successful Windows regression runs. No concrete issue remains to block merging.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: preventing Windows asynchronous-handle read crashes in storage.
Description check ✅ Passed The description directly explains the Windows crash fix, regression tests, portability updates, and validation results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@apotema

apotema commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

VERDICT: APPROVED
Reviewed head: 2c4b66d08db945b0fd914194b073a66fa49eee79. Reviewer: Claude (Anthropic LLM).

Findings (no blockers)

  • src/storage/files.zig:159: the flag is correct against Zig 0.16 std. Threaded.zig dirOpenFileWindows opens with .IO = if (follow_symlinks) .SYNCHRONOUS_NONALERT else .ASYNCHRONOUS but always returns .flags = .{ .nonblocking = false } (the .lock = .none early return, ~L5104). std.Io.File.Flags documents that nonblocking = true on Windows means "opened with MODE.IO.ASYNCHRONOUS", so std is violating its own invariant, and the PR restores it. With the flag false, readFilePositionalWindows hits .PENDING => unreachable. With it true, reads take the APC + waitForApcOrAlert path. File.Reader defaults to .positional, so NtReadFile always gets a byte offset. This matters because an async handle keeps no current file position.
  • The no-follow and symlink protection is kept. .OPEN_REPARSE_POINT = !follow_symlinks is unchanged, and the stat().kind != .file check at :154 still runs before the flag is set. fileStatWindows maps a surrogate reparse point to .sym_link.
  • There is no leak. errdefer file.close(io) at :153 covers the fallible stat, and setting the flag can't fail. close doesn't depend on the flag.
  • The POSIX path is unchanged. It already builds the file with nonblocking = true at :168.
  • Tests: setModified uses the mtime, which is the only field purge reads (stagingIsStale(stat.mtime, …)). The symlink-placeholder cleanup and the linked skip at test/storage_test.zig:243 only affect the fixture. The new large-payload test at :85 runs on every host.
  • Placement is fine. The change stays inside the existing os == .windows or .wasi branch of openKey. No workflow changes; the Windows matrix leg already existed.
  • Nit, non-blocking: this works around a std bug, so the comment could mention dropping the line once std sets the flag itself. Setting it stays correct either way.

Ran

  • zig build test --summary all on macOS arm64 exited 0: 276/276 steps, 1503/1503 tests.
  • zig build test-storage --summary all exited 0: 19/19 pass.
  • CI run 36730816067 on this head passed. The build-and-test (windows-latest) log shows zig build test --summary all, which includes the storage tests, at 133/133.

Not run: the Windows runtime. I'm on macOS, so for that I relied on CI and on reading the std source.

Bot inline comments: none. CodeRabbit and the Codex connector posted summary comments only.

@apotema
apotema merged commit 293c664 into main Sep 30, 2026
3 checks passed
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.

1 participant