Skip to content

win32: keep replacement temp private until commit - #568

Open
SantanDon wants to merge 1 commit into
nih-at:mainfrom
SantanDon:security/win32-temp-private-until-commit
Open

win32: keep replacement temp private until commit#568
SantanDon wants to merge 1 commit into
nih-at:mainfrom
SantanDon:security/win32-temp-private-until-commit

Conversation

@SantanDon

Copy link
Copy Markdown

Summary

Keep Windows replacement temporary files unreadable until the archive replacement has committed.

The named Win32 source currently opens a replacement temp with FILE_SHARE_READ. During a normal archive rewrite, another process can therefore open the sibling temp and read newly written archive contents before zip_close() commits the replacement.

Reproduction

I tested current main at 31eefd70f27d33a442466914f5919c84784eafd8 with a 128 MiB stored entry containing a unique marker. While zip_close() was writing the replacement, a second process enumerated the sibling victim.zip.* temp and requested a compatible GENERIC_READ handle.

On current code the reader succeeded 13.7 ms after temp creation and read the new marker while the writer was still running; the writer then committed normally.

Simply removing sharing is not sufficient: the current commit path closes the temp handle before MoveFileEx(), which leaves a small close-before-rename window. A reader can win that race, read the fully written temp, and its handle can make the rename fail.

Fix

Open replacement temps with FILE_SHARE_DELETE and keep the temp handle open through MoveFileEx(). This lets Windows rename the open temp while denying concurrent read/write opens for the temp's entire pre-commit lifetime. The handle is closed after the rename, with failure paths closing it before returning.

This is atomic-replacement visibility hardening, not an ACL bypass. The temp continues to use the existing destination security descriptor when available; the change prevents principals that are already permitted by that DACL from observing the new archive state through the temporary path before the replacement commits.

Verification

  • final focused run: 640 concurrent open attempts, 0 successful reads, ERROR_SHARING_VIOLATION observed, writer exit 0
  • 10/10 repeated race runs: 0 successful pre-commit reads and 10 successful commits
  • final archive readback: 128 MiB entry and marker preserved; no leftover temp files
  • native nihtest/CTest suite: 190 Test Passed. entries, 0 Test Failed. entries
  • full static Windows build completed
  • git diff --check clean

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