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
+20 -20
View File
@@ -89,8 +89,8 @@ void forth_FREEZE_WORD(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
return; /* Silently fail on invalid length */
@@ -130,8 +130,8 @@ void forth_UNFREEZE_WORD(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
return;
@@ -168,11 +168,11 @@ void forth_FROZEN_QUERY(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
vm->data_stack[vm->dsp++] = 0; /* Not frozen */
vm_push(vm, 0); /* Not frozen */
return;
}
@@ -180,9 +180,9 @@ void forth_FROZEN_QUERY(VM *vm) {
DictEntry *entry = name ? vm_find_word(vm, name, (size_t)len) : NULL;
if (entry && (entry->flags & WORD_FROZEN)) {
vm->data_stack[vm->dsp++] = -1; /* True (frozen) */
vm_push(vm, -1); /* True (frozen) */
} else {
vm->data_stack[vm->dsp++] = 0; /* False (not frozen) */
vm_push(vm, 0); /* False (not frozen) */
}
}
@@ -208,9 +208,9 @@ void forth_HEAT_STORE(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t heat = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
cell_t heat = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
return;
@@ -245,11 +245,11 @@ void forth_HEAT_FETCH(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
vm->data_stack[vm->dsp++] = 0;
vm_push(vm, 0);
return;
}
@@ -257,9 +257,9 @@ void forth_HEAT_FETCH(VM *vm) {
DictEntry *entry = name ? vm_find_word(vm, name, (size_t)len) : NULL;
if (entry) {
vm->data_stack[vm->dsp++] = entry->execution_heat;
vm_push(vm, entry->execution_heat);
} else {
vm->data_stack[vm->dsp++] = 0;
vm_push(vm, 0);
}
}
@@ -285,8 +285,8 @@ void forth_SHOW_HEAT(VM *vm) {
return;
}
cell_t len = vm->data_stack[--vm->dsp];
cell_t caddr = vm->data_stack[--vm->dsp];
cell_t len = vm_pop(vm);
cell_t caddr = vm_pop(vm);
if (len <= 0 || len > WORD_NAME_MAX) {
return;
@@ -391,7 +391,7 @@ void forth_DECAY_RATE_FETCH(VM *vm) {
}
/* Stack: ( -- rate ) */
vm->data_stack[vm->dsp++] = (cell_t)DECAY_RATE_PER_US_Q16;
vm_push(vm, (cell_t)DECAY_RATE_PER_US_Q16);
}
/**