Skip to content

fix(builtins): read_bytes_buf over-cap raises io error + [path, max_bytes] opt-in (#601) - #605

Merged
InauguralPhysicist merged 1 commit into
mainfrom
fix-read-bytes-buf-cap-601
Jul 14, 2026
Merged

fix(builtins): read_bytes_buf over-cap raises io error + [path, max_bytes] opt-in (#601)#605
InauguralPhysicist merged 1 commit into
mainfrom
fix-read-bytes-buf-cap-601

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Closes #601

What

  1. Over-cap reads RAISE. read_bytes_buf on a file over its cap returned null — indistinguishable from "file missing" — so a >10 MB asset died far downstream with a misleading diagnosis (DeslanStudio: a 3-minute beat imported as "not a WAV file"). The load_file: missing path returns null without raising (silent success) #490matmul: shape/type errors return null without raising #512 direction: it now raises a catchable io error naming the actual size, the active cap, and the escape hatch.
  2. Replay-safe raise. The observed file size rides the tape as a VAL_NUM N record (unambiguous: success records a VAL_BUFFER, open-failure records null); under EIGS_REPLAY the identical error is re-derived from the tape with no live fs access. Argument validation moved before the tape boundary (deterministic on both paths, consumes no record). Documented in docs/TRACE.md.
  3. Bounded opt-in: read_bytes_buf of [path, max_bytes], hard ceiling 512 MB. A VAL_BUFFER stores one double per byte (8x fs→RAM amplification; 512 MB of file ≈ 4 GiB resident), so the ceiling bounds the worst case while blocking no realistic asset. Bad max_bytes raises value. 1-arg form keeps the 10 MB default — security posture unchanged.
  4. Sandbox posture pinned: the fail-closed allowlist never contained read_bytes_buf and still doesn't; test_sandbox_allow.eigs now probes it explicitly so the posture can't silently widen.

Missing/unopenable files keep the existing null contract.

Tests

tests/test_read_bytes_cap.sh (9 checks, wired into the suite): over-cap raise with size+cap in the message; opt-in read of a sparse 12 MB file; cap enforced against the opt-in value; max_bytes validation (0 / >512 MB / non-numeric); missing-file null pinned; record/replay byte-identity with the file deleted between record and replay.

Gates

  • Release suite: 2841/2880 — the only failure was test_proc_stream case 8a, a pre-existing spin-wait race under load (filed test_proc_stream case 8a is timing-flaky under load: spin-wait for /bin/true exit #604; passes 3/3 in isolation; unrelated to this diff).
  • ASan/UBSan detect_leaks=1: 2882/2884 — leak tally 0 (no NOTE line); the 2 failures were examples/invariant_multi_agent.eigs / invariant_weak.eigs hitting the [97] 60 s wall guard under ASan while an unrelated heavy process pinned a core; both pass in isolation (58 s / 71 s under ASan on the N3350 — marginal against the guard on this box, and neither touches read_bytes_buf).
  • Doc gates [99]/[99b] green (BUILTINS.md row updated; stdlib index consistent).

🤖 Generated with Claude Code

…ytes] opt-in (#601)

A file over the 10 MB cap returned null — indistinguishable from "file
missing" — so a >10 MB asset died far downstream with a misleading
diagnosis (DeslanStudio: a 3-minute WAV imported as "not a WAV file").
The #490#512 direction: over-cap now RAISES a catchable `io` error
naming the actual size and the active cap.

The raise survives replay: the observed size rides the tape as a
VAL_NUM N record (unambiguous — success records a VAL_BUFFER,
open-failure records null) and the identical error is re-derived under
EIGS_REPLAY with no live fs access. Argument validation moved before
the tape boundary (deterministic, consumes no N record on either path).

New bounded opt-in `read_bytes_buf of [path, max_bytes]` raises the
ceiling for real assets. Hard ceiling 512 MB: a VAL_BUFFER stores one
double per byte (8x fs->RAM amplification — 512 MB of file is ~4 GiB
resident), so the ceiling bounds the worst case while blocking no
realistic asset. Bad max_bytes raises `value`. The 1-arg form keeps
the 10 MB default — existing security posture unchanged — and the
fail-closed sandbox allowlist still blocks read_bytes_buf entirely
(posture pinned in test_sandbox_allow.eigs).

Missing/unopenable files keep the existing null contract.

Tests: tests/test_read_bytes_cap.sh (9 checks) — over-cap raise with
size+cap in the message, opt-in >10 MB read (sparse 12 MB file), cap
enforced against the opt-in value, max_bytes validation, missing-file
null, and record/replay byte-identity with the file deleted between
runs. Wired into run_all_tests.sh.

Closes #601

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@InauguralPhysicist
InauguralPhysicist merged commit 29a31c5 into main Jul 14, 2026
17 checks passed
@InauguralPhysicist
InauguralPhysicist deleted the fix-read-bytes-buf-cap-601 branch July 14, 2026 08:17
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.

read_bytes_buf: over-cap read silently returns null (misleading downstream errors) + no opt-in for files >10MB

1 participant