Skip to content

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

Description

@avrabe

Summary

The #746 fix (0.43.0) stopped synth from skipping wide static-region loads under --native-pointer-abi, but the relocated copy path now silently miscompiles: it runs and produces WRONG bytes for part of a static buffer copy. This is a regression from loud skipsilent wrong data, which for a safety-relevant backend is the worse failure mode.

Found dissolving gale's v0.4.0 fused gust:os {time, log} node (the buffer-carrying syscall seam #746 was meant to unblock). The node now dissolves cleanly (13 functions, 0 skipped, only read32/write32 undefined) — but the emitted object copies the wrong bytes.

Symptom (deterministic, qemu cortex-m3)

The app copies the static string "gust:os up\n" into a Vec<u8> and the provider writes it byte-by-byte to an mmio sink. Captured on the sink:

got      = [2, 0, 0, 0, 1, 0, 0, 32, 117, 112, 10]
expected = [103,117,115,116,58,111,115, 32, 117, 112, 10]   ("gust:os up\n")

Bytes 7–10 (" up\n") are correct; bytes 0–6 ("gust:os") are garbage — and the garbage is the .data content immediately AFTER the string:

arm-none-eabi-objdump -s -j .data:
 0020 67757374 3a6f7320 75700a00 02000000   gust:os up......
                                   ^^^^^^^^ the 02 00 00 ... that leaked into bytes 0-6

So the copy's source address is miscomputed (offset past the string) for the head chunk; the tail chunk reads correctly. run()'s unrelated return value and the byte count (11) are both correct — only the copied content is wrong.

Isolation (synth ARM backend, nothing upstream)

  • The pre-synth wasm is correct. Running fused.wasm AND loom.wasm (i.e. immediately before synth compile) under a wasmtime host that stubs gust:hal/mmio returns byte-exact "gust:os up\n". So component-new ×3 → wac plug → meld 0.41 fuse → loom 1.2 inline all produce functionally correct wasm; only synth compile corrupts it.
  • The .data is placed correctly (string is byte-exact in the object). The bug is in the CODE that copies it, traced to the inlined Vec::extend_from_slice/memmove copy loop (func_17, reached run → func_20 → func_16 RawVec-grow → func_17), or in the relocated static source address feeding it.
  • Not ABI/r11: verified an r11=0 (fp) trampoline compiles correctly; failure is byte-identical with and without it.
  • A direct single i64.load from a static (a 12-line repro) codegens correctly under 0.43 — so this is specific to the multi-chunk copy path, not every wide static load.

Repro

gale benches/gust/drivers/build-os-tl.sh (synth 0.43.0, meld 0.41.0, loom 1.2.0; S1/S2 surgeries removed) produces os-tl-cm3.o; benches/gust/src/bin/gust_os_tl_probe.rs is the qemu oracle. Committed at gale feat/gust-v0.4.0-syscall-seam @ ef68c59. Happy to help reduce to a standalone minimal case — the direct-i64.load toy doesn't trigger it, so it needs the copy-loop shape (static source ≥ ~9 bytes, chunked head+tail copy above wasm_data_base).

Impact

v0.4.0 buffer-carrying syscall seam remains blocked — #746 changed the failure mode from skip to silent-wrong, but the node still doesn't run correctly. Blocks every buffer-carrying capability (log/channel/io). The stateless gust:os/time node (no buffer copy) is unaffected and passes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions