Fix out-of-bounds stack pushes/pops in four vendored word files (found during S-1a)

physics_freeze_words.c, dictionary_heat_diagnostic_words.c,
physics_diagnostic_words.c, physics_benchmark_words.c treated dsp as a
count (data_stack[dsp++] push, data_stack[--dsp] pop) while the VM's dsp
is the top index. On an empty stack a push wrote data_stack[-1], 8 bytes
before the VM struct -- POST's DECAY-RATE@ test did it every boot (on
aarch64 it hit parity.c's counter, elsewhere the POST block RAM tail).
All 28 sites now use vm_pop/vm_push; four one-item guards fixed (dsp<0).

Evidence on amd64/aarch64/riscv64: bytes before the VM struct unchanged
across POST; DECAY-RATE@ leaves 1 item; LOOKUP-STRATEGY! now works;
G interleave, precedence, console under load, relay 3, dict_hash unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-24 11:54:14 -04:00
co-authored by Claude Opus 5.5
parent d6c90a6839
commit 9bf2e8ec95
18 changed files with 60436 additions and 33 deletions
+58
View File
@@ -87,3 +87,61 @@ Evidence is named **before** each stage runs.
**Not run, by design:** the busy-wait DoE driver, which would deadlock under O-1 (§1).
Don't run `DOE` until O-C.
- [x] **Stack-convention fix: out-of-bounds stack writes in four vendored word files** (found
during S-1a, 2026-09-24). Captain Bob: *"Fix now, own commit."*
**The defect.** The VM's `dsp` is the index of the top item (−1 = empty):
`vm_pop` reads `data_stack[dsp--]`. Four vendored files instead treated `dsp` as a count,
pushing with `data_stack[vm->dsp++] = x` (11 sites) and popping with
`data_stack[--vm->dsp]` (17 sites):
- `physics_freeze_words.c`: 7 pushes, 12 pops;
- `dictionary_heat_diagnostic_words.c`: 4 pushes, 2 pops;
- `physics_diagnostic_words.c`: 1 pop;
- `physics_benchmark_words.c`: 1 pop.
**The consequences:**
- On an empty stack a push wrote `data_stack[-1]`, **8 bytes before the VM struct**. POST's
own `DECAY-RATE@.basic` test does exactly that on every boot.
- Otherwise a push overwrote the top item and left `dsp` one slot off.
- The pops read the item *below* the top, and on the last pop, `data_stack[-1]`.
- Four one-item words also guarded with `dsp < 1`, which demands two items, so
`LOOKUP-STRATEGY!`, `COMPARE-LOOKUPS`, `PHYSICS-BURN` and `BENCH-DICT-LOOKUP` always
errored on valid input.
**How it was found.** The S-1a parity probe's incomplete counter read one too many on
aarch64 only.
- A per-test hook in the test runner named `DECAY-RATE@.basic` as the test after which
the counter moved (`logs/20260924-104424/aarch64/`).
- Writing a marker into `mama->data_stack[-1]` changed the counter to the marker
(`-105120`).
- Printing runtime addresses (`-105729`) showed why aarch64 only: the aarch64 kernel runs
inside the `lld-link` PE, whose `.bss` puts `parity.c`'s counter directly before
`sk_mama_vm`.
- On amd64 and riscv64 the same write lands in the last cell of the POST block-RAM buffer,
silently.
- An interim hypothesis of memory aliasing, drawn from ELF symbol addresses that don't
describe the aarch64 runtime image, was wrong and is withdrawn.
**The fix:**
- Every site now uses the VM's own `vm_pop(vm)` / `vm_push(vm, x)`, which check bounds.
- The four one-item guards are now `dsp < 0`.
- The two- and three-item guards (`dsp < 1`, `dsp < 2`) were already correct for the
top-index convention and are unchanged.
**Evidence.** A probe (reverted) compared the 8 bytes before the VM struct across POST,
plus console checks, on all three ISAs (`logs/20260924-111124/amd64/`, `-112508/aarch64/`,
`-113949/riscv64/`):
- `STACKFIX PROBE: … UNCHANGED across POST`;
- `DECAY-RATE@ DEPTH . .` gives `1 1`;
- `S" DUP" FROZEN? DEPTH . .` gives `1 0`;
- `HEAT@` leaves 1 item, and `HEAT-PERCENTILES` leaves 3;
- `1 LOOKUP-STRATEGY! LOOKUP-STRATEGY@ .` gives `1`, depth 0, where it used to error;
and `0 …` gives `0`.
Acceptance in the same boots:
- the O-A checks: G interleave A = 40/B = 40/48 runs, `HHH` precedence, and the typed line
running mid-stream;
- relay `3`, idle growth 0;
- `dict_hash` unchanged (531, `0x572daac83b797332`);
- POST passes;
- clean kernel and hosted builds.