fix(builtins): read_bytes_buf over-cap raises io error + [path, max_bytes] opt-in (#601) - #605
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #601
What
read_bytes_bufon 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) #490–matmul: shape/type errors return null without raising #512 direction: it now raises a catchableioerror naming the actual size, the active cap, and the escape hatch.VAL_NUMNrecord (unambiguous: success records aVAL_BUFFER, open-failure records null); underEIGS_REPLAYthe 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.read_bytes_buf of [path, max_bytes], hard ceiling 512 MB. AVAL_BUFFERstores 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. Badmax_bytesraisesvalue. 1-arg form keeps the 10 MB default — security posture unchanged.read_bytes_bufand 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_bytesvalidation (0 / >512 MB / non-numeric); missing-file null pinned; record/replay byte-identity with the file deleted between record and replay.Gates
test_proc_streamcase 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).detect_leaks=1: 2882/2884 — leak tally 0 (no NOTE line); the 2 failures wereexamples/invariant_multi_agent.eigs/invariant_weak.eigshitting 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 touchesread_bytes_buf).🤖 Generated with Claude Code