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:
co-authored by
Claude Opus 5.5
parent
d6c90a6839
commit
9bf2e8ec95
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user