fix(#757): overlapping active data segments bind reloc to the wrong segment — silent miscompile on gale's fused node#779
Merged
Conversation
…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
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.
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.wasmdeclares three active data segments all at wasm linmem0x100000. 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 linmem0x100008. The#354mixed-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 stale0x02) 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
seg_0+8 → linmem 0x100008 reads 0x02, runtime owns 'g' (0x67); fixed GREEN. The emitted reloc flipsseg_0+8→seg_2+8(seg_0 relocs 1→0, seg_2 13→14).#739/#746/#758/#406) all PASS.mem757_gale_differential.pypinsloom.wasmas 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