Repository navigation
fix(jq): abort error-preview traversal at budget instead of walking hostile values - #2699
Conversation
…ostile values dump_trunc's Capped sink swallowed over-budget writes with Ok, so Val's Display (?-laden format_val!/write_seq!) kept expanding shared-structure values (e.g. 2^40-element math operands) just to discard the output. write_str now reports fmt::Error once the 15-byte budget is spent, which aborts the traversal, and the ... marker is unconditional on over-budget previews. Small-value output is byte-identical.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | cd84d5d | Commit Preview URL Branch Preview URL |
Oct 12 2026, 07:03 AM |
Required original-case verification before mergeThe The original finding is the compact shared-value expression Record finite jq compatibility, including small/exact-fit/over-limit multibyte errors and their expected truncation contract, plus cancellation/limit-denial and recovery on the actual reusable API/session path. A helper called twice with two fresh contexts is fresh-invocation recovery; label it honestly rather than same-session proof. The new ellipsis behavior on a partial UTF-8 prefix should have a documented/explicit oracle contract, not only a byte-count assertion. Read all full issue/formal/inline comments, resolve each obligation, and require unchanged full gates and actual required CI conclusions. The existing watcher pipes through tail/echo, so its reported zero alone is not evidence every required check succeeded. Preserve expected raw jq error5 separately from harness0 and all initial failed probe/compile receipts. Actual merged-revision build/assertions and durable absolute logs are still required for independent cloud closure. This is the same finding/PR, not a request for a second issue or broad formatter redesign. Current remaining proof on cd84d5d (07:13 UTC)The exact shared-tree CLI regression, explicit UTF-8 contract, and honestly relabeled fresh-context tests are now present; credit those repairs. The new same-shell recovery test is also a genuine improvement. Do not redo these repaired product changes. The original bounded-child obligation is not yet discharged by the new launcher: Reuse the existing fail-closed child helper or an equivalent small launcher for these hostile cases, with concurrent capped pipes, truthful failure flags and finite cleanup under the shared end time. Retain effective CPU/stack readback and a supported memory bound or active RSS cutoff in the evidence harness. Assert successful reader/reap completion and no oversize/read error/timeout as well as expected jq error5 and same-shell recovery0. The Windows path needs a bounded equivalent rather than silently losing the existing regression. If some evidence lives in a separate harness, retain its actual absolute logs/producer and assertion exits; do not call the wall-only test all-bounds proof. No stack increase, test skip, broad formatter redesign, additional issue, universal production process-tree contract or repeat of the already repaired exact-filter/UTF-8 objections is requested. Independent bounded comparison now retained (07:45 UTC)Exact jq object-key merge47f8666514325eb2a2a33e35d77e38c25ba535cf was independently rebuilt with pinned Rust1.95 (actual build0). The original million-byte shared string x10,000 rejected-key expression, its caught-error form and the depth40 shared nested-key expression each exhausted effective childCPU10 (raw SIGXCPU/-24; assertion harness1), with finite owned cleanup and sampled peakRSS below28MiB. This is demonstrated CPU-limit exhaustion in a bounded child, not a demonstrated OOM or an uncontrolled production hang. Small-key/valid-key/same-shell finite controls passed. The same six cases on independently rebuilt cd84d5d passed assertion harness0/raw expected5 or0; original0.874s, nested0.058s. Effective childPID limitsCPU10/stack8MiB/file2MiB, wall30/active sampled RSS256MiB cutoff and finitecleanup were attested; binary/source hashes unchanged before/after. Both full receipts/logs/executed harnesses/binaries retained at the finding's Credit the genuine early-stop fix; the old jq-key finding must not be closed on47f86665 alone, and no redundant jq-key product agent/PR is needed because this same formatter repair addresses the residual traversal. The existing required fixture-bound/complete-review/exact-merged/report obligations above remain; no additional product redesign, new issue or repeated reproduction of a separately fixed native finding is requested. |
…iew oracle Address review on the error-preview budget fix: - cli_oneshot bounded-child regression runs the exact finding filter (reduce range(0;40) shared tree) with deadline poll + kill; asserts raw exit 5, sub-KB stderr, truncation marker; a regression fails by timeout/kill without OOMing the harness. - True same-session recovery: one shell hits a truncated huge-operand error, then the same session continues; fresh-context helpers relabeled honestly. - dump_trunc oracle contract documented (verbatim vs 11-byte prefix + unconditional ... marker, UTF-8 boundary rule).
|
Addressed in cd84d5d — point by point:
|
…2700) Follow-up to #2699 (same finding, test-only, no product change). Replaces the wall-only poll and unbounded recovery run with the existing fail-closed child helper, extended minimally: portable helper (signal readback stays unix-gated), elapsed/RSS readback fields, exact-finding shared-tree test and same-session recovery test asserting fail-closed flags, raw exit 5/0, sub-KB output, truncation marker, exec-budget elapsed, and RSS bound on unix.
What changed
Bounds the CPU cost of jq math/index error previews, not just their allocation.
Why
Follow-up to #2697: that PR capped preview allocation with the
Cappedsink, but the sink swallowed over-budget writes withOk(()), soVal'sDisplay(the?-ladenformat_val!/write_seq!machinery) kept expanding the full hostile value just to discard the output. For the finding's shared-structure input (reduce range(0; 40) as $i (1; [., .]) | . + 1, a ~2^40-element expansion on type-mismatch), the error path still walked the whole expansion.Before / After (production entry point,
timeout 120, direct child timing)Before (by source inspection + failing test
preview_writer_aborts_once_over_budget):write!over a 200 KB value returnedOkafter walking all of it.After:
Exit code 5 matches real jq's runtime-error exit; small-value wording is unchanged (
math_errors_use_jq_wordingpasses).Also fixed: over-budget previews that aborted before filling the buffer (multibyte char straddling the cut) previously returned without the
...marker; the marker is now unconditional on over-budget output. Small-value output is byte-identical.Validation
cargo test -p bashkit --lib --features jq builtins::jq: 357 passed, 0 failed (6 new tests: early-abort, exact-fit, just-over, multibyte, recovery, huge-operand session integration)cargo fmt --check,cargo clippy --all-targets --features jq: cleanjust check-okf: conforms; knowledge note updated inknowledge/runtimes/jaq-json-vendor.mdChecklist