RQ-61-IMMRANGE (#1072): per-site closure of the six release-invisible immediate-range guards — one genuinely unguarded (aarch64 expected class id), four demonstrated, one unreachable - #1117
Merged
Conversation
Site: encoder.rs:470 debug_assert!(imm12 < 0x1000, "cmp imm12 out of range"). Of its three selector callers, two were already bounded (br_table's chain index by the BR_TABLE_MAX_TARGETS decline; the OOB guard's table_slots by substrate::plan's MAX_TABLE_SLOTS decline). The third — the type guard's EXPECTED structural class id — was bounded nowhere: substrate::plan checks only the SLOT ids (funcref_class_ids), and the (1) comment claiming "the class ids were range-checked by substrate::plan" was half-false, the #946 unverified-enforcement shape #1072 names. Red-first, in RELEASE (debug_assert compiled out): a module with 4096 distinct structural signatures whose call_indirect expects the 4096th compiled with exit 0 and emitted 0x7140_023F = `cmp w17, #0, lsl #12` — the id's bit 12 landed in the sh field (bit 22), imm12 read 0, and a NULL slot (reserved id 0) would PASS the WASM Core 4.4.8 type check it must trap on. The debug binary panicked at encoder.rs:470 on the same input. Fix: loud selector decline (expected > substrate::MAX_CLASS_ID), the same bound plan pins for slot ids; boundary 4095 still lowers, pinned by test. Refs #1072 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
The MAX_CLASS_ID bound on funcref_class_ids is the upstream guard the aarch64 dispatch cites for the sidecar values the type guard LOADS; it was stated and never tripped (#946 shape). Mutation-checked: disabling the plan check turns this test red. Refs #1072 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Comment-only, zero byte change. aarch64 cmp_imm: name the three callers' loud upstream declines and their tripping tests. aarch64 add_ext_uxtw_sh: reachability verdict — sole caller passes the literal 3, no module-derived value reaches the shift. ARM A32+Thumb-2 type-check asserts: the "selector enforces X" claim is now DEMONSTRATED, not assumed — sole Some producer resolve_runtime_type_check declines id > 255 / offset > 4095, test_676_call_indirect_runtime_check_range_declines trips both, and both declines were mutation-checked (disabling either turns the test red). Release-mode failure shape recorded: the encoder masks truncate (id 256 compares as 0 — a NULL slot would pass the 4.4.8 check), not panic. Refs #1072 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…cord status_evidence R4 demanded acknowledgement of the delivery commits. done-when restated to #1072's own three-outcome closure list (the filed wording demanded Err at all six sites, which the issue itself ranks below a demonstrated guard and the artifact's own description rules out as the RQ-61-MVPANIC class); the release-mode red-first requirement is kept. verified-by records the exact oracles and mutation checks. Also: clippy useless_vec in the new substrate test. Refs #1072 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
RQ-61-IMMRANGE: per-site closure of the six release-invisible immediate-range guards
Refs #1072
debug_assert!is compiled out in release. Per the issue's own list, each ofthe six residual sites gets exactly one of: (1) a DEMONSTRATED upstream guard,
(2) promotion to a loud decline, (3) a stated unreachability recorded at the
assert. A grep was treated as a hypothesis throughout: every enforcement claim
below is backed by a tripping test, and every tripping test was
mutation-checked (guard disabled -> test red -> restored).
Per-site verdicts
encoder.rs:470cmp_immimm12BR_TABLE_MAX_TARGETS(16) loud decline; tripped bybr_table_residuals_loud_decline_by_nameencoder.rs:470cmp_immimm12substrate::plan'sMAX_TABLE_SLOTS(4095) decline over the whole contiguous region; tripped byoversized_table_declines; aplanErr aborts the aarch64 compile before any bytes shipencoder.rs:470cmp_immimm12substrate::planbounds only the SLOT ids (funcref_class_ids); the expected id was bounded nowhere, and the selector comment claiming "the class ids were range-checked by substrate::plan" was half-false — the #946 shape the issue predicted. Red-first release evidence below. Boundary 4095 still lowers, pinned by testencoder.rs:484add_ext_uxtw_shshiftTABLE_SLOT_BYTES = 8); no module-derived value reaches the parameterarm_encoder.rs:193/194(A32)expected_id<= 255 /type_off<= 4095Some((expected_id, type_off))producer in shipped code isresolve_runtime_type_check(instruction_selector.rs), which loud-declines id > 255 and sidecar offset > 4095;test_676_call_indirect_runtime_check_range_declinestrips BOTH declines, and disabling either turns it red (mutation-checked). Comments at the sites now cite the guard, the test, and the release failure shapearm_encoder.rs:3932/3933(Thumb-2)type_checktuple. Comment updated to matchThe release failure shape differs by ISA and is now recorded at each site:
the ARM encoders MASK (
& 0xFF/& 0xFFF), so an out-of-range value wouldsilently TRUNCATE — id 256 compares as 0 and a NULL slot (reserved id 0)
would pass the WASM Core 4.4.8 type check. The aarch64 encoder does not mask,
so overflow bits SHIFT into neighbouring instruction fields.
Red-first, in RELEASE, for the promoted site
Input: a generated legal module with 4096 distinct structural signatures
(1 dispatch type + 4094 six-param fillers +
$weird), a 3-slot table holdingonly class-1 functions plus a null slot, and
call_indirect (type $weird)—$weird's first-occurrence class id is 4096. Every upstream gate passes(
planchecks slot ids only).Before the fix,
./target/release/synth compile -b aarch64 --all-exportsexited 0 and emitted, at the dispatch's type guard:
0x7140023F: the expected id's bit 12 landed in bit 22, thesh(shift-LSL-#12) field of SUBS-immediate, leaving the imm12 field 0. The
type guard compares the loaded slot class id against 0, so a NULL slot
(reserved id 0) PASSES the type check it must trap on and the dispatch
branches into the null slot's trampoline. The debug binary panicked at
encoder.rs:470on the same input — the exact debug/release divergence theissue names.
After the fix the same release binary exits 1:
Acceptance / reach coupling
The new decline fires only for a module with > 4095 distinct structural
signatures whose
call_indirectexpects a high-class type, on aarch64. Nopreviously CORRECT accept can newly decline: any module hitting this decline
previously mis-encoded in release (or panicked in debug). Decline-sensitive
oracles re-run on the freshly built fixed binary:
aarch64_m2_decline_538.py— 13/13 declined ops loud-declined, PASSaarch64_call_indirect_851_differential.py— 35 checks / 6 exports vswasmtime incl. all three 4.4.8 traps, PASS
No corpus
.watadded;EXPECTED_DECLINESuntouched.Also in this PR
oversized_slot_class_id_declines:substrate::plan'sMAX_CLASS_IDslot-id bound was stated but never tripped by a test (the doc-vs-source sweep: 30 mechanically-verified disagreements, 4 of them possible defects (not doc staleness) #946 shape one
layer down) — added, mutation-checked.
artifacts/release-v0.61/RQ-61-IMMRANGE.yamlflipped toimplemented;its done-when restated to the issue's own three-outcome closure list (the
filed wording demanded
Errat all six sites, which the issue itself ranksbelow a demonstrated guard and the artifact's own description rules out as
the RQ-61-MVPANIC class);
verified-byrecords the oracles and mutationchecks. No new artifact — no
ARTIFACT_FLOORchange.Gates run locally (all exit-code gated)
cargo fmt --check,cargo clippy --workspace --all-targets -- -D warningscargo test --workspace --features riscvcargo build --release --features riscv(the red-first repro's build)python3 scripts/claim_check.py claims.yaml— 54/54python3 scripts/status_evidence_check.py— 0 failuresrivet validate— 40 errors, ALL the unloadable-local-externalscross-repo class (
.rivet/repos/galehas norivet.yamlin thisworktree), none about this change; the federated CI job is advisory
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L