Fix double-fopen in open-with and writable? for + modes#11
Conversation
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.
There was a problem hiding this comment.
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
-
open-withfix (file.carp:224-227) — correct.IO.Raw.fopenreturns(Ptr FILE), an unmanaged raw pointer in Carp. Reusingfafter the null check is fine — no ownership issue. The old code calledfopentwice, leaking the first handle on every successful open and creating a TOCTOU race. The fix eliminates both problems. -
writable?fix (file.carp:209-212) — correct. The+modifier makes anyfopenmode 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, soopen-withreturnsResult.Errorbeforewritable?is ever called. Notably,readable?(line 204) already had the+check — the asymmetry was the bug. -
Tests are well-chosen. The
r+writable/readable tests verify the fix directly. Thew+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). -
No other bugs found in the surrounding code.
Verdict: merge
Both fixes are correct, well-tested, and address real bugs. Ship it.
What
Two bug fixes in
File:open-withdouble-fopen:open-withcalledfopentwice — once for the null check, then again when constructing theFilestruct. The first handle was silently leaked on every successful open, and the pattern created a TOCTOU race where the secondfopencould fail even though the first succeeded. Now reuses the checked handle directly.writable?missing+mode:writable?only checked forwandain the mode string, but the+modifier (as inr+,w+,a+) also makes a file writable. Now checks for+as well, consistent with howreadable?already handles it.Tests
Added three new tests:
writable?returns true forr+modereadable?returns true forr+modeopen-withwithw+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.