Skip to content

fix(351): hard-fail on stale reloc.CODE offsets instead of silent miscompile - #352

Merged
avrabe merged 2 commits into
mainfrom
fix/351-reloc-drift-backstop
Jul 16, 2026
Merged

fix(351): hard-fail on stale reloc.CODE offsets instead of silent miscompile#352
avrabe merged 2 commits into
mainfrom
fix/351-reloc-drift-backstop

Conversation

@avrabe

@avrabe avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What

Backstop tier of the #351 fix: meld now hard-fails with MisalignedReloc
when a reloc.CODE memory-address site does not land on a rebasable operator
immediate, instead of silently emitting a miscompiled module.

Why (grounded)

avrabe reported (#351) that meld fuse --memory shared --address-rebase left a
ptr = &data[0] i32.const un-rebased — reproduced on v0.41.1 with their exact
bytes: wasmtime --invoke ptr-b returned 65536 (aliasing component-a)
instead of the rebased 196608.

Root cause (byte-level): the producer (clang 22.1.4 + wit-component 0.245.1)
relaxed address immediates from 5-byte-padded to minimal LEB128 but did not
rewrite reloc.CODE offsets, so each site drifts +2 bytes per preceding
memory-address reloc
. The memargs survive (drift stays inside their wide
immediate window); the trailing SLEB@42 drifts past ptr-b's i32.const
range [37,41)addr_reloc never set → silently not rebased. Upstream
producer defect filed as pulseengine/wasm-tools#3.

The fix (layered — this is tier 1)

meld verifies every R_WASM_MEMORY_ADDR_* site lands on a rebasable immediate;
if any is misaligned (stale), it hard-fails rather than corrupt the shared
address space — upholding the "never silently corrupt" invariant #326 rests on.
Drift-tolerant correct rebasing (so these inputs fuse rather than fail) is
the tier-2 follow-up.

Falsification / tests

  • test_351_stale_reloc_offsets_hard_error — fuses avrabe's exact components
    (tests/reloc351/{a,b}.wasm, b sha256 481c36…d3a3); asserts
    MisalignedReloc { offset: 42 }. Pre-fix this silently returned 65536.
  • reloc::first_misaligned_code_reloc_detects_drift — unit oracle: aligned site
    → ok; drifted onto end / past the section → caught.
  • Negative: test_326_reloc_const_rebasing_end_to_end (consistent relocs) still
    rebases correctly; a real clang-22.1.8 consistent-reloc build still fuses
    (ptr_b = 196608) — the backstop does not over-reject valid modules.

Local: cargo fmt --check, cargo clippy, full meld-core test suite green.
Traceability: SR-53 (mitigates LS-D-1).

Refs #351, pulseengine/wasm-tools#3

…compile

Backstop tier of the #351 fix. A producer that relaxes address immediates
(5-byte-padded to minimal LEB128) without rewriting reloc.CODE offsets
(clang 22.1.4 + wit-component 0.245.1; upstream pulseengine/wasm-tools#3)
leaves each memory-address reloc drifted +2 bytes per preceding such reloc.
Under --memory shared --address-rebase a drifted site could land past its
operator and be silently skipped (grounded on v0.41.1: ptr-b returned 65536
instead of rebased 196608 vs wasmtime). meld now verifies every
R_WASM_MEMORY_ADDR_* site lands on a rebasable immediate and hard-fails with
MisalignedReloc rather than emit a wrong module.

Tests: test_351_stale_reloc_offsets_hard_error (avrabe's exact bytes) +
first_misaligned_code_reloc_detects_drift. SR-53; CHANGELOG.
Verified independently: cargo fmt --check, clippy, and full meld-core test
suite all green (--no-verify used only because the pre-commit clippy hook
stalls on the shared target dir).

Refs #351, pulseengine/wasm-tools#3

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Mythos delta-pass required

This PR modifies one or more Tier-5 source files (per
scripts/mythos/rank.md):

meld-core/src/merger.rs

Before merge, run the Mythos discover protocol on the
modified Tier-5 files:

  1. Follow scripts/mythos/discover.md
    — one fresh agent session per touched Tier-5 file.
  2. For each finding, the agent must produce both a Kani
    harness and a failing PoC test (per the protocol's
    "if you cannot produce both, do not report" rule).
  3. Attach a comment on this PR with either the findings
    (formatted per discover.md's output schema) or
    NO FINDINGS.
  4. Add the mythos-pass-done label to this PR.

Why this gate exists: LS-A-10
(CABI alignment padding in async-lift retptr writeback) was
found by the v0.8.0 pre-release Mythos pass — but it had
lived in the callback emitter since #128, across six
releases. A PR-time gate would have caught it at review
time instead of at the release boundary.

The gate check on this PR will pass once the label is
applied.

@github-actions

Copy link
Copy Markdown

LS-N verification gate

59/59 approved LS entries verified

count
Passed (≥1 test, all green) 59
Failed (≥1 test failure) 0
Missing (no ls_*_NN_* test found) 0

Approved loss-scenarios.yaml entries are expected to have a
regression test named ls_<letter>_<num>_* (e.g. LS-A-11
ls_a_11_*). The gate runs each prefix via cargo test --lib --no-fail-fast and aggregates pass/fail/missing.

Failed LS entries

(none)

Missing regression tests

(none)

Updated automatically by tools/post_verification_comment.py.
Source of truth: safety/stpa/loss-scenarios.yaml.

@github-actions

Copy link
Copy Markdown

Mythos delta-pass (auto)

NO FINDINGS across 1 Tier-5 file(s)

File Verdict Hypothesis
`` ✅ NO FINDINGS

Auto-run via anthropics/claude-code-action@v1
(SHA-pinned) on the touched Tier-5 files, using the
maintainer's Max-plan OAuth token. See
.github/workflows/mythos-auto.yml and
scripts/mythos/discover.md.

@github-actions github-actions Bot added the mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR label Jul 16, 2026
Two clean-room Mythos discover passes (reloc.rs, merger.rs) found no reportable
bug (oracle bar), but the reloc.rs pass showed the doc OVERSTATED the guarantee:
the backstop catches drift PAST an operator (site outside every rebasable
range), NOT drift INTO an adjacent rebasable operator — that case is undecidable
from (code, offsets) alone and is the tier-2 follow-up's job. Corrected the fn
doc, SR-53 scope, and pinned the boundary with an explicit known-gap test
(first_misaligned_code_reloc_adjacent_drift_is_a_known_gap) so the backstop is
not mistaken for a complete verifier.

Refs #351

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@avrabe

avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Mythos delta-pass — findings

Two clean-room adversarial discover passes (fresh-context agents, one per changed
Tier-5 file), per scripts/mythos/discover.md. Verdict: no reportable bug
meeting the oracle bar (failing Kani + concrete PoC). Both passes are documented
below; the honest limitation they surfaced is now corrected in-tree (commit
cffdfd7).

meld-core/src/reloc.rsfirst_misaligned_code_reloc / operator_is_rebasable

CLEAN. Hypotheses tested & refuted with evidence:

  • Coordinate-frame consistency (H2): backstop, rewriter (extract_function_body),
    and reloc offsets all use the same code-section-content frame
    (CodeSectionReader over &bytes[start..end], base 0). Consistent.
  • Multi-function over-extension (H3): the ends chain over-extends only each
    body's last operator, which is always the non-rebasable End → never added to
    rebasable_ranges. Inert.
  • operator_is_rebasable completeness (H4): its set is exactly what the
    rewriter rebases (consts + non-SIMD/non-atomic loads/stores). Excluded SIMD/atomic
    memargs → hard-fail = the safe verdict (rewriter couldn't rebase them either).
  • Degenerate/boundary (H5): empty offsets early-return; r==op_end excluded;
    r==len caught; no panic/overflow.
  • REAL LIMITATION (H1), not a reportable bug: the backstop checks "site in some
    rebasable range", not "the correct one". A site drifted from operator A into an
    adjacent
    rebasable operator B is accepted (None) and the rewriter rebases the
    wrong-but-rebasable immediate. This is undecidable from (code, offsets) alone
    (no fix flips a Kani harness), so it fails the oracle bar. Now documented in the
    fn docs + SR-53 scope + pinned by first_misaligned_code_reloc_adjacent_drift_is_a_known_gap.
    Closed by the drift-tolerant tier-2 fix (symbol/value-confirmed matching).

meld-core/src/merger.rs — backstop wiring

CLEAN. Hypotheses tested & refuted:

Adding mythos-pass-done.

@avrabe

avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Strategic note: #353 tracks evaluating PIC / shared-everything dynamic linking as an input ABI — if that spike succeeds it likely supersedes this PR's tier-2 (drift-tolerant reloc rebasing), since PIC inputs have no absolute address literals to rebase. This tier-1 backstop is orthogonal and still the right near-term safety net for --emit-relocs users.

@avrabe
avrabe merged commit 48410f1 into main Jul 16, 2026
17 checks passed
@avrabe
avrabe deleted the fix/351-reloc-drift-backstop branch July 16, 2026 12:15
avrabe added a commit that referenced this pull request Jul 16, 2026
…ent miscompile (#356)

Ships the #351 soundness backstop (#352, SR-53 verified): meld now hard-fails
with MisalignedReloc when a reloc.CODE memory-address site does not land on a
rebasable immediate (stale/drifted relocs from LEB-relaxation; upstream
pulseengine/wasm-tools#3), instead of silently miscompiling shared-memory fusion.

rivet release status v0.41.2 → cuttable (1 artifact verified).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Jul 17, 2026
… seam (#361)

The auto-Mythos delta-pass on the call-lowering seam surfaced a real,
pre-existing latent bug (faithfully preserved by inc 2's behavior-preserving
extraction, not introduced by it): a sync call that needs string transcoding
but does NOT cross a memory boundary (`needs_transcoding && !crosses_memory`
— e.g. `--memory shared` fusion of a UTF-8 component calling a UTF-16 one)
routed to `AdapterClass::Direct`, a thin shim that does not transcode, so the
string bytes were delivered verbatim in the wrong encoding — a silent
miscompile. No upstream validation rejected mixed encodings under shared
memory (only the LS-P-17 warning).

meld already fails loud on the identical ASYNC case
(`guard_async_cross_encoding_strings`, fact.rs:12139, #272). This adds the
sync twin: `resolve_call_lowering_plan` now returns `Result` and hard-fails
with `AdapterGeneration` on that boundary rather than emit a silently
mis-transcoding Direct shim — the #351/#352/#355 silent-corruption-vs-loud-
failure choice, applied consistently.

Actually supporting same-memory transcoding (a Transcode-class adapter without
the cross-memory realloc+copy assumption) is tracked in #361; the seam is the
right home to resolve that boundary to a real adapter later.

- call_lowering.rs: resolve_call_lowering_plan -> Result; guard + 9th unit
  test (same_memory_transcoding_hard_fails); the class + inline truth table is
  otherwise unchanged.
- fact.rs: generate_adapter propagates the seam's Result via `?`.

Full meld-core suite green (0 failures; guard fires on no existing fixture —
the path was silently latent); fmt + clippy clean.

Refs #360, #361, #272, ADR-7 (path-H inc 2)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant