Skip to content

fix: prevent potential fd double-close in _write_checkpoint - #98

Closed
sylearn wants to merge 2 commits into
aiming-lab:mainfrom
sylearn:fix/checkpoint-fd-double-close
Closed

sylearn wants to merge 2 commits into
aiming-lab:mainfrom
sylearn:fix/checkpoint-fd-double-close

Conversation

@sylearn

@sylearn sylearn commented Mar 19, 2026 •

Copy link
Copy Markdown

Summary

  • _write_checkpoint uses tempfile.mkstemp() which returns a raw fd, then passes it to open(fd, "w")
  • If open() succeeds but write() fails, the with block closes the fd via __exit__, then the except handler calls os.close(fd) again
  • On systems where fd numbers are quickly reused, this can close an unrelated file descriptor (silent data corruption risk)

Fix: Close the mkstemp fd immediately and reopen by path. The temp file already exists on disk, so there is no TOCTOU race. This eliminates fd ownership ambiguity entirely.

Test plan

  • Verified mkstemp creates the file atomically (no race between close and reopen)
  • Confirmed the atomic rename via Path.replace() still works correctly

mkstemp() returns a raw fd which is then passed to open(fd, "w").
If open() succeeds but the write fails, the with-block closes the fd,
then the except handler calls os.close(fd) again. On systems where fd
numbers are quickly reused, this can close an unrelated file descriptor.

Simplify by closing the mkstemp fd immediately and reopening by path.
The temp file already exists on disk so there is no TOCTOU race.

Made-with: Cursor
Copilot AI review requested due to automatic review settings March 19, 2026 04:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the atomic checkpoint writer in researchclaw.pipeline.runner to avoid ambiguous ownership of a raw file descriptor returned by tempfile.mkstemp(), preventing a potential double-close scenario during error handling.

Changes:

  • Close the mkstemp() file descriptor immediately after creation.
  • Reopen the temp file by path for JSON writing, then atomically replace checkpoint.json.
  • Simplify exception cleanup to only remove the temp file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread researchclaw/pipeline/runner.py Resolved
Comment on lines 84 to 86
try:
with open(fd, "w", encoding="utf-8") as fh:
with open(tmp_path, "w", encoding="utf-8") as fh:
fh.write(json.dumps(checkpoint, indent=2))
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@sylearn

sylearn commented Mar 19, 2026

Copy link
Copy Markdown
Author

Consolidated into #102 as requested.

@sylearn sylearn closed this Mar 19, 2026
@sylearn
sylearn deleted the fix/checkpoint-fd-double-close branch March 19, 2026 06:40
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.

2 participants