Found by iLambdaAi's grader-hardening review (round 14, 2026-08-17), executed A/B proof.
dict_set interns every key via env_intern_name into the process-global g_env_name_interns table, freed only at thread-detach/state-destroy — never between sandbox_runs — and sandbox_charge never sees it. Within one run the key-string extraction is bounded by the charged strbuf chokepoint, but the interned copy is permanent: a long-lived host running many sandbox_runs over untrusted input (the grade() rejection-sampling farm) accumulates unbounded, unreclaimed host memory while every individual run reports ok:1.
Measured: 20,000 runs each decoding an 8-key object with per-run-distinct keys → peak RSS 27.5MB; identical file with keys reused across runs → 7.2MB. Delta ≈ 125 bytes per distinct key, linear and unbounded in run count. On a 4GB farm box, thousands of generations reach the uncatchable x_oom abort with clean grades reported throughout.
Fix directions considered (none trivial, hence an issue rather than a commit on the #963 train):
- charge interned bytes under an armed sandbox — but the per-run budget resets while the allocation persists, so the accounting would need to be process-global/persistent;
- epoch the intern table at the sandbox boundary and free run-added interns at teardown — unsafe naively, since keys interned during the run can escape in the returned result's dicts;
- per-dict key ownership (no interning) when
g_sandbox_active — cleanest semantically, but changes dict key lifecycle/compare assumptions.
Interim mitigation shipped in iLambdaAi (lib/validate.eigs residual note): farm drivers recycle the grading process every few thousand generations and run under ulimit -v.
Found by iLambdaAi's grader-hardening review (round 14, 2026-08-17), executed A/B proof.
dict_setinterns every key viaenv_intern_nameinto the process-globalg_env_name_internstable, freed only at thread-detach/state-destroy — never betweensandbox_runs — andsandbox_chargenever sees it. Within one run the key-string extraction is bounded by the charged strbuf chokepoint, but the interned copy is permanent: a long-lived host running manysandbox_runs over untrusted input (thegrade()rejection-sampling farm) accumulates unbounded, unreclaimed host memory while every individual run reportsok:1.Measured: 20,000 runs each decoding an 8-key object with per-run-distinct keys → peak RSS 27.5MB; identical file with keys reused across runs → 7.2MB. Delta ≈ 125 bytes per distinct key, linear and unbounded in run count. On a 4GB farm box, thousands of generations reach the uncatchable x_oom abort with clean grades reported throughout.
Fix directions considered (none trivial, hence an issue rather than a commit on the #963 train):
g_sandbox_active— cleanest semantically, but changes dict key lifecycle/compare assumptions.Interim mitigation shipped in iLambdaAi (lib/validate.eigs residual note): farm drivers recycle the grading process every few thousand generations and run under
ulimit -v.