Skip to content

Fix double-fopen in open-with and writable? for + modes#11

Merged
hellerve merged 1 commit into
masterfrom
claude/fix-open-with-and-writable
May 25, 2026
Merged

Fix double-fopen in open-with and writable? for + modes#11
hellerve merged 1 commit into
masterfrom
claude/fix-open-with-and-writable

Conversation

@carpentry-agent

Copy link
Copy Markdown

What

Two bug fixes in File:

  1. open-with double-fopen: open-with called fopen twice — once for the null check, then again when constructing the File struct. The first handle was silently leaked on every successful open, and the pattern created a TOCTOU race where the second fopen could fail even though the first succeeded. Now reuses the checked handle directly.

  2. writable? missing + mode: writable? only checked for w and a in the mode string, but the + modifier (as in r+, w+, a+) also makes a file writable. Now checks for + as well, consistent with how readable? already handles it.

Tests

Added three new tests:

  • writable? returns true for r+ mode
  • readable? returns true for r+ mode
  • Read-write round-trip via open-with with w+ mode (exercises both fixes together)

All 12 non-walk tests pass. The 4 walk test failures are pre-existing (missing test/nested/fixture).


Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

open-with called fopen twice: once for the null check, then again in the
File constructor. The first handle was leaked on every successful open and
created a TOCTOU race. Now reuses the checked handle directly.

writable? only checked for w and a modes, missing the + modifier that makes
any base mode read+write (r+, w+, a+). Now checks for + as well.

@carpentry-reviewer carpentry-reviewer Bot 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.

Build & Tests

Build: pass. 12 of 16 tests pass — the 4 walk test failures (walk works on directory, walk-with works on directory, map works on directory, contents works on directory) are pre-existing on master (9/13 pass there, same 4 fail). The 3 new tests all pass. CI passes on both ubuntu-latest and macos-latest.

Findings

  1. open-with fix (file.carp:224-227) — correct. IO.Raw.fopen returns (Ptr FILE), an unmanaged raw pointer in Carp. Reusing f after the null check is fine — no ownership issue. The old code called fopen twice, leaking the first handle on every successful open and creating a TOCTOU race. The fix eliminates both problems.

  2. writable? fix (file.carp:209-212) — correct. The + modifier makes any fopen mode read-write. The fix adds (> (String.index-of m \+) -1) as the first disjunct. Edge cases check out: "rb" → false, "r+" → true, "w+" → true. The concern about bare "+" is moot — fopen("file", "+") fails, so open-with returns Result.Error before writable? is ever called. Notably, readable? (line 204) already had the + check — the asymmetry was the bug.

  3. Tests are well-chosen. The r+ writable/readable tests verify the fix directly. The w+ roundtrip test is the strongest validation — the old double-fopen code would produce an empty read because write went to handle #1 and read came from handle #2 (separate file positions).

  4. No other bugs found in the surrounding code.

Verdict: merge

Both fixes are correct, well-tested, and address real bugs. Ship it.

@hellerve hellerve merged commit 7c031f2 into master May 25, 2026
2 checks passed
@hellerve hellerve deleted the claude/fix-open-with-and-writable branch May 25, 2026 17:09
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