fix(#288): wire trap_gate onto the runtime path (additive trap backstop)#289
Open
avrabe wants to merge 1 commit into
Open
fix(#288): wire trap_gate onto the runtime path (additive trap backstop)#289avrabe wants to merge 1 commit into
avrabe wants to merge 1 commit into
Conversation
trap_gate (#279/#284) had ZERO callers — its transform-checking entry points were dead code, so trap soundness rested entirely on the per-fold STATIC guards (#273/#274/#276/#278/#281). This wires the certificate-checked trap-equivalence gate onto the constant_folding runtime path as an ADDITIVE proof BACKSTOP, gated behind the `verification` feature. What is wired - New `loom_core::trap_backstop` module: for every div/rem-on-constants fold that FIRES in constant_folding, ordeal must PROVE the op was trap-free (LRAT certificate re-checked) via the new `trap_gate::check_div_discard`. A non-accepted verdict reverts that function. Routed kinds: div_s, div_u, rem_u (ordeal models their trap condition exactly). - constant_folding calls the backstop after the fold, before the Z3 value validator (which cannot see traps — the reason the miscompiles slipped). Design: purely additive - No static guard is relaxed. When a guard holds (no trapping fold fires), behavior is unchanged. The backstop only catches a trapping fold that slipped a guard. With `verification` off the module is compiled out and behavior is byte-identical. Documented remaining gaps (static guard stays sole authority) - rem_s: ordeal's trap_div over-approximates it (shares the INT_MIN/-1 overflow disjunct with div_s, but WASM rem_s(INT_MIN,-1) does NOT trap). Routing it would falsely reject a safe fold. Pinned by a trap_gate test. - mem-discard (#278): the vacuum load-drop fold never fires (is_pure_pusher excludes loads), so there is no live fold to back-check; check_mem_transform stays exercised by its own unit tests only. - select-arm-discard (#281): the fold lives in loom-shared/rewrite_pure below the ordeal boundary; its discarded arm's trap condition is not reconstructed at the instruction layer. Its static is_no_trap_expr guard stays authority. Anti-regression tests (#288's whole point) - test_288_* assert the gate is INVOKED on the runtime path: a known-safe fold (20/4) is observed AND accepted; a constructed trapping fold (div_s(INT_MIN,-1), div_u(x,0)) is BLOCKED with the static guard bypassed in-test, proving the gate — not the static guard — is the runtime authority. - New check_div_discard unit tests (accept trap-free, reject overflow, reject ÷0, pin the rem_s over-approximation). rivet: adds TEST-TRAP-GATE-RUNTIME-PATH (verifies REQ-1/REQ-6, mitigates UCA-5/UCA-21, verifies CC-4/CC-20; release tag v122). `rivet validate` PASS. Builds (macOS homebrew Z3): default (verification) + explicit --features verification + --no-default-features all green; clippy clean; fmt applied. Fixes #288 Refs #279 Co-Authored-By: Claude Opus 4.8 <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.
Summary
trap_gate(the systemic trap-equivalence gate, #279/#284) was dead code — its transform-checking entry points (check_div_transform,check_mem_transform,check_select_discard) had zero callers. Trap soundness therefore rested entirely on the per-fold static guards (#273/#274/#276/#278/#281); the gate provided zero runtime protection (#288, #279 false-closed on the belief it was the authority).This PR wires the certificate-checked gate onto the
constant_foldingruntime path as an additive proof backstop, gated behind theverificationfeature.What was wired
loom_core::trap_backstopmodule. For every div/rem-on-constants fold that fires inconstant_folding, ordeal must prove the op was trap-free (orig.may_trap ⇔ false, LRAT certificate re-checked) via the newtrap_gate::check_div_discard. A non-accepted verdict reverts that function's fold.div_s,div_u,rem_u— ordeal'strap_divmodels their trap condition exactly (concrete constant operands ⇒ no symbolic slack).Additive-backstop design (no capability change)
Every existing static guard is kept untouched (the
is_simple_pure_instrdiv/rem exclusion, thediv_s(INT_MIN,-1)const-fold refusal inrewrite_pure, the vacuum may-trap-load keep, the selectis_no_trap_exprguard). When a guard holds, no trapping fold fires and behavior is unchanged. The backstop only catches a trapping fold that slipped a guard. With theverificationfeature off,trap_backstopis compiled out entirely and behavior is byte-identical.Documented remaining gaps (static guard stays sole authority)
rem_sis deliberately not routed: ordeal'strap_divover-approximates it — it shares theINT_MIN/-1overflow disjunct withdiv_s, but per the WASM specrem_s(INT_MIN,-1)does not trap (result 0). Routing it would falsely reject a genuinely-safe fold and revert a valid optimization (a capability regression). Its exact static constant-folding eliminates the mandatory div_s(INT_MIN,-1) overflow trap — optimized module returns INT_MIN where the original traps (i32 + i64) #273 guard (which folds it to 0) remains authority. Pinned bytrap_gate::tests::rem_s_int_min_is_over_approximated_rejectedso a future ordeal fix is noticed. (Filed as tool friction against ordeal in spirit — the disjunct is spurious for rem.)load; dropfold never fires (is_pure_pusherexcludes loads), so there is no live fold to back-check at that site.check_mem_transformstays exercised by its own unit tests only — loom carries no symbolic memory bound at the instruction layer to feed it soundly on a live fold.loom-shared'srewrite_pure, below the ordeal dependency boundary, and the discarded arm is a value term whose trap condition loom does not reconstruct at the instruction layer. Its staticis_no_trap_exprguard remains sole authority.Per the conservatism rule, sites that cannot be modeled soundly with the existing API + a small clearly-correct helper were left on their static guard and documented rather than modeled dubiously.
Anti-regression tests (the point of #288)
test_288_backstop_observes_and_accepts_safe_div_fold— the gate observes the20/4fold fire and accepts it.test_288_backstop_blocks_trapping_div_fold— a constructeddiv_s(INT_MIN,-1)anddiv_u(x,0)fold is blocked by the gate with the static guard bypassed in-test, proving the gate (not the static guard) is the runtime authority.test_288_constant_folding_pass_keeps_safe_fold— end-to-end, the pass with the backstop live still folds a trap-free const div (no over-blocking).check_div_discardunit tests: accept trap-free, reject overflow, reject ÷0, pin therem_sover-approximation.rivet
Adds
TEST-TRAP-GATE-RUNTIME-PATH(verifies REQ-1/REQ-6, mitigates UCA-5/UCA-21, verifies CC-4/CC-20; release tagv122).rivet validate→ PASS.Build / test results (macOS homebrew Z3, isolated target dir), honestly stated
cargo test --release -p loom-core(default = includesverification):test result: ok. 480 passed; 0 failed; 2 ignored(lib suite) — all green.cargo test --release -p loom-core --features verification:test result: ok. 480 passed; 0 failed; 2 ignored— all green.cargo test --release -p loom-core --no-default-features(backstop compiled out, proves byte-identical path compiles + passes): all green.cargo clippy --release -p loom-core --features verification: clean;cargo fmt --allapplied.Do not merge — for user review.
Fixes #288
Refs #279
🤖 Generated with Claude Code