Repository navigation
fix(tests): cap bounded-child RSS instead of address space - #2701
Merged
Merged
Conversation
The `awk_field_depth_bounded` launcher capped the child's address space with `ulimit -v 2097152`. AddressSanitizer reserves roughly 14 TiB of *virtual* address space for its shadow map during startup, so the instrumented child died in `ReserveShadowMemoryRange` before executing a line of test code. The test then failed on the launcher's own cap rather than on the depth bound it asserts, which is why nightly run 257 went red while required CI, which is not instrumented, stayed green on the same commit -- the same divergence shape as the CLI-path bug in run 252. `awk_field_depth_tests.rs` was the only harness still using such a cap. Memory containment moves to a parent-side peak-RSS watchdog that samples the live child each poll, kills it on breach and asserts the peak afterwards, so a child that breached on its final poll and then exited on its own still fails closed. CPU, stack, core, output-drain and wall bounds are unchanged. RSS is also the better proxy for the runaway allocation the bound exists to catch and costs nothing in virtual space, so one mechanism holds on instrumented and native builds alike; it is the convention the other bounded-child harnesses already use. The replacement bound is not vacuous: the child's measured peak is 29,216 KiB over roughly four polls in a 45 ms lifetime, against a 2 GiB ceiling (the same figure as the old cap, now measured on resident rather than virtual memory), which an ASAN build fits while multi-gigabyte growth still trips it. A static scan now fails any launcher that reintroduces an address-space cap, in either the shell spellings or a direct `setrlimit`, with a `// rlimit-as-ok: <reason>` hatch for one that provably never builds with a sanitizer. Verified by running nightly's own invocation (`-Z build-std`, `-Z sanitizer=address`) on this change: 3 passed, 0 failed, where `awk_field_depth_bounded` previously failed. The mechanism was reproduced standalone with a 3-line ASAN binary, which fails under the old cap with the byte-identical `0xdfff0001000` reservation from the nightly log and exits 0 without it. Reintroducing the cap fails the new scan. Claude-Session: https://claude.ai/code/session_013mA6kgZvfp234w1L2gLint
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | dd6bea0 | Commit Preview URL Branch Preview URL |
Oct 12 2026, 09:21 AM |
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.
What changed
awk_field_depth_bounded's supervised-child launcher no longer caps the child'saddress space. Memory containment moves to a parent-side peak-RSS watchdog that
samples the live child, kills it on breach, and asserts the peak afterwards, so
growth still fails closed. CPU, stack, core, output-drain and wall bounds are
unchanged.
A static scan test now fails any test launcher that reintroduces an
address-space cap, in either the shell spellings (
ulimit -v,ulimit -d) or adirect
setrlimit(RLIMIT_AS,RLIMIT_DATA), with an escape hatch(
// rlimit-as-ok: <reason>) for a launcher that provably never builds with asanitizer.
Why
The nightly AddressSanitizer job went red on run 257 while required CI stayed
green on the same commit (
fa2262d). The launcher setulimit -v 2097152;AddressSanitizer reserves ~14 TiB of virtual address space for its shadow map
during startup, so the instrumented child died in
ReserveShadowMemoryRangebefore executing a line of test code. The test then failed on the launcher's own
cap rather than on the depth bound it asserts — the same divergence shape as the
CLI-path bug in run 252, and
awk_field_depth_tests.rswas the only harnessstill using an address-space cap.
RSS is also the better proxy for the runaway allocation the bound exists to
catch, and it costs nothing in virtual space, so one mechanism holds on
instrumented and native builds alike.
Before / After
Root cause, reproduced standalone (the failure is in ASAN startup, independent of
bashkit code):
The reservation size
0xdfff0001000is byte-identical to the one in the nightlylog, confirming the same mechanism.
Before, under the real nightly invocation (
-Z build-std,-Z sanitizer=address):After, same invocation and flags on this branch:
The replacement bound is not vacuous: the child's measured peak RSS is 29,216 KiB
over ~4 polls in a 45 ms lifetime, against the 2 GiB ceiling (the same 2 GiB
figure as the old cap, now measured on resident rather than virtual memory), so
an ASAN build fits comfortably while multi-gigabyte growth still trips it.
Guard verified by mutation — reintroducing the cap fails the scan with the
offending line:
Risk
an RSS bound it did not have. The removed cap is the one that could not
coexist with a sanitizer.
starts and ends between two polls is unmeasured.
status.success(), the stackand CPU limits and the wall watchdog still fail such a run, and the depth guard
under test is what bounds the recursion in the first place.
Checklist
https://claude.ai/code/session_013mA6kgZvfp234w1L2gLint
Generated by Claude Code