Skip to content

feat(layout): --stack-layout=low — stack at the SRAM bottom, overflow BusFaults instead of corrupting linmem/globals (#687, VCR-MEM-003) - #694

Merged
avrabe merged 1 commit into
mainfrom
feat/687-stack-layout-low
Jul 10, 2026
Merged

feat(layout): --stack-layout=low — stack at the SRAM bottom, overflow BusFaults instead of corrupting linmem/globals (#687, VCR-MEM-003)#694
avrabe merged 1 commit into
mainfrom
feat/687-stack-layout-low

Conversation

@avrabe

@avrabe avrabe commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #687. Relates #404/#406 (MPU-protectable region model), VCR-MEM-001/002.

What

Today's self-contained Cortex-M image sets the initial SP at the top of SRAM growing down toward the R9 globals table and linear memory — a stack overflow silently corrupts them (red evidence below). This lands option 1 of the #687 graded menu:

Applicability (chosen: refuse loudly, documented): self-contained images only. --relocatable, import-forced ET_REL, and non-Cortex-M backends error — their linker script/harness owns the layout, so warn-and-ignore would ship a flag that silently did nothing.

Layout contract documented on build_multi_func_cortex_m_elf (both layout tables + invariants, including the pre-existing 0x100 offset between the startup R11 base and the optimized path's absolute base — preserved verbatim in both layouts), cross-referenced from the #650/#669 R11 table contract on CallIndirectGuards.

Oracles (the issue's acceptance — all run locally, new CI job stack-layout-687-oracle)

scripts/repro/stack_layout_687_differential.py runs the REAL image (its own vector-table SP + reset path) under unicorn, both layouts:

case result
RED (high, today's hazard) deep recursion silently clobbers 4/8 linmem canaries before any fault — faults only at the SRAM floor (SP 0x1FFFFFF0)
GREEN (low) UC_ERR_WRITE_UNMAPPED below SRAM (SP 0x20000010) with 8/8 canaries intact — the BusFault precedes any linmem damage
TRANSPARENT in-budget calls (optimized-path abs-base loads/stores, direct-path recursion, arithmetic) match wasmtime under both layouts
SHIFT PIN low-layout canary addresses = high-layout addresses + stack_size exactly (0x2000F1000x20010100)

Existing fixture under the flag: the #649 i64-global-init differential (real startup, stateful wasmtime ground truth) passes unchanged under EXTRA_SYNTH_FLAGS="--stack-layout low" (startup R9 0x200100000x20011000) — the layout shift is transparent when the stack doesn't overflow.

Flag-off (untouched by construction — verified)

  • Frozen anchors 10/10 (frozen_codegen_bytes).
  • Whole-ELF cmp vs a main-built binary: byte-identical self-contained images for stack_canary_687.wat, i64_global_init_649.wat, control_step.wasm.
  • StackLayout::High reserve = 0 degenerates every formula; the startup's parametrized MOVW/MOVT R11 encodes the historical fixed bytes (pinned by test_minimal_startup_generation); CompileConfig::linmem_base defaults to the historical 0x2000_0100.
  • Unit tests: shifted startup bases, SP-init vector-table words per layout, full resolve_stack_layout refusal contract.

Rivet

VCR-MEM-003 added to artifacts/verified-codegen-roadmap.yaml: the stack-guard ladder (low layout → MPU guard region → v8-M PSPLIM), status implemented for the low-layout rung only; rungs 2/3 are future issues. rivet validate: 0 errors (one house-style WARN, same class as the file's other 98).

Gates

cargo test --workspace 2083 passed / 0 failed · clippy -D warnings clean · fmt clean · rebased onto 6caca5a (#689) with all gates re-run.

🤖 Generated with Claude Code

… BusFaults instead of corrupting linmem/globals (#687, VCR-MEM-003)

Today's self-contained Cortex-M image puts the initial SP at the TOP of
SRAM growing down toward the R9 globals table and linear memory — a stack
overflow silently corrupts them (unicorn red evidence: deep recursion
sweeps the linmem canaries and only faults once SP exits SRAM).

--stack-layout=low (+ --stack-size, default 4096) reserves the stack at
the SRAM BOTTOM: SP init = SRAM start + stack_size, and the ENTIRE
RAM-anchored layout — startup R11 linmem base, R9 globals table, RAM
auto-scale, AND the optimized path's absolute 0x2000_0100 base (new
CompileConfig::linmem_base threaded to the OptimizerBridge) — shifts UP
by stack_size as one. Overflow descends past 0x2000_0000 into reserved
space and BusFaults on the FIRST errant push — every Cortex-M, no MPU.

Applicability: self-contained images ONLY. --relocatable, import-forced
ET_REL, and non-Cortex-M backends REFUSE the flag loudly (their linker
script/harness owns the layout). Default high stays byte-identical by
construction (reserve=0 degenerates every formula; startup MOVW/MOVT R11
encodes the historical fixed bytes) — frozen anchors 10/10 and whole-ELF
cmp vs main on three self-contained fixtures confirm.

Layout contract documented on build_multi_func_cortex_m_elf, cross-
referenced from the #650/#669 R11 table contract (CallIndirectGuards).

Oracle (scripts/repro/stack_layout_687_differential.py, CI-wired):
  RED   high: recursion silently clobbers 4/8 linmem canaries BEFORE any
        fault (fault only at the SRAM floor, SP 0x1FFFFFF0)
  GREEN low: UC_ERR_WRITE_UNMAPPED below SRAM (SP 0x20000010) with 8/8
        canaries intact — the BusFault precedes any linmem damage
  TRANSPARENT: in-budget calls match wasmtime under BOTH layouts; the
        existing #649 global-init differential passes unchanged under
        EXTRA_SYNTH_FLAGS="--stack-layout low" (R9 0x20010000→0x20011000)
  SHIFT PIN: low canary addresses = high addresses + stack_size exactly

VCR-MEM-003 artifact added: the stack-guard ladder (low layout → MPU
guard → v8-M PSPLIM), status implemented for the low-layout rung only.

Relates #404/#406 (MPU-protectable region model).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.70899% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-cli/src/main.rs 94.25% 10 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit f13d1ae into main Jul 10, 2026
37 checks passed
@avrabe
avrabe deleted the feat/687-stack-layout-low branch July 10, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant