Skip to content

fix(#757): overlapping active data segments bind reloc to the wrong segment — silent miscompile on gale's fused node#779

Merged
avrabe merged 1 commit into
mainfrom
fix/757-overlapping-segment-reloc
Jul 16, 2026
Merged

fix(#757): overlapping active data segments bind reloc to the wrong segment — silent miscompile on gale's fused node#779
avrabe merged 1 commit into
mainfrom
fix/757-overlapping-segment-reloc

Conversation

@avrabe

@avrabe avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

The #757 silent miscompile, finally reproduced and fixed — gale delivered the exact fused module (loom.wasm), which the 7 synthetic reconstructions (PR #772) provably could not hit.

Root cause

loom.wasm declares three active data segments all at wasm linmem 0x100000. WASM applies active segments in order, so the last (seg_2) overwrites the earlier two and owns those bytes at runtime — the string "gust:os up\n" lives at linmem 0x100008. The #354 mixed-split reloc retargeting (main.rs) resolved each static access to its segment via .position() (first match), so the string source bound to __synth_wasm_seg_0 + 8 (seg_0's stale 0x02) instead of __synth_wasm_seg_2 + 8. Runtime: got=[2,0,0,0,1,0,0,32,…] instead of "gust:os up\n".

Fix

.position().rposition() — resolve overlapping addresses to the last-declared owning segment (WASM overwrite semantics). Byte-identical for non-overlapping segments (first==last match), so zero regression.

Verification

  • Red→green on gale's exact module, byte-exact to the symptom: pre-fix seg_0+8 → linmem 0x100008 reads 0x02, runtime owns 'g' (0x67); fixed GREEN. The emitted reloc flips seg_0+8seg_2+8 (seg_0 relocs 1→0, seg_2 13→14).
  • Frozen anchors 10/10 byte-identical; existing static-data differentials (#739/#746/#758/#406) all PASS.
  • New mem757_gale_differential.py pins loom.wasm as a permanent, non-vacuous CI regression fixture (reconstructs runtime linmem, asserts every static-data reloc reads the runtime-correct byte).

North Star

This is the #739/#746/#757/#758 patch-accretion cluster — filed VCR-VER-003 (#777) to translation-validate static-data addressing per-compilation and make the whole class unrepresentable.

Closes #757.

🤖 Generated with Claude Code

…ST-declared owner

gale's fused os-tl node (loom.wasm) declares THREE active data segments ALL at
wasm linear-memory offset 0x100000. In WASM, active segments apply in declaration
order, so a LATER segment OVERWRITES an earlier one — seg_2 (last, 24 B) owns those
bytes at runtime and holds "gust:os up\n" at linmem 0x100008.

The #354 mixed-split reloc retargeting (main.rs) mapped each `__synth_wasm_data + C`
static access to a per-segment `__synth_wasm_seg_K` symbol by finding the segment
whose wasm-offset range contains C — via `.position()`, the FIRST match. With
overlapping segments that binds an address to the earliest segment, not the one
that owns it at runtime: the string source at 0x100008 bound to
`__synth_wasm_seg_0 + 8` (seg_0's stale const 0x02) instead of
`__synth_wasm_seg_2 + 8` (the string). Runtime: got=[2,0,0,0,1,0,0,32,...] instead
of "gust:os up\n" — a silent miscompile, byte-identical across 0.43.0/0.43.1/0.44.0/
0.45.0 and NOT reproducible by synthetic reconstructions (PR #772: 7 shapes green).

Fix: `.position()` -> `.rposition()` — resolve overlapping addresses to the
LAST-declared containing segment, matching WASM overwrite semantics. For
non-overlapping segments every address is in exactly one, so first==last match and
the emitted bytes are byte-identical (frozen anchors 10/10, all existing static-data
differentials green — #739/#746/#758/#406 unaffected).

Oracle: scripts/repro/mem757_gale_differential.py pins gale's exact loom.wasm as a
permanent CI regression fixture. It reconstructs runtime linmem (segments applied
in order, later-wins) and asserts every static-data reloc reads the runtime-correct
byte — RED on pre-fix (seg_0+8 reads 0x02, runtime owns 'g'=0x67), GREEN on fix.
Non-vacuous by construction; the real module lives in CI because the synthetic
shapes provably don't cover it.

Follow-up North Star lane filed: VCR-VER-003 (#777) — per-compilation translation
validation of static-data addressing, so this whole #739/#746/#757/#758 cluster
becomes unrepresentable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@avrabe
avrabe merged commit fa9a30e into main Jul 16, 2026
39 checks passed
@avrabe
avrabe deleted the fix/757-overlapping-segment-reloc branch July 16, 2026 04:52
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.43.0: #746 fix relocates but MISCOMPILES the wide-static copy path — silent wrong bytes (was: skip)

1 participant