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
@@ -77,9 +77,9 @@ void forth_HEAT_PERCENTILES(VM *vm) {
return;
}
vm->data_stack[vm->dsp++] = vm->heat_threshold_25th;
vm->data_stack[vm->dsp++] = vm->heat_threshold_50th;
vm->data_stack[vm->dsp++] = vm->heat_threshold_75th;
vm_push(vm, vm->heat_threshold_25th);
vm_push(vm, vm->heat_threshold_50th);
vm_push(vm, vm->heat_threshold_75th);
}
/**
@@ -100,7 +100,7 @@ void forth_LOOKUP_STRATEGY_FETCH(VM *vm) {
return;
}
vm->data_stack[vm->dsp++] = (cell_t)vm->lookup_strategy;
vm_push(vm, (cell_t)vm->lookup_strategy);
}
/**
@@ -116,12 +116,12 @@ void forth_LOOKUP_STRATEGY_FETCH(VM *vm) {
void forth_LOOKUP_STRATEGY_STORE(VM *vm) {
if (vm->error) return;
if (vm->dsp < 1) {
if (vm->dsp < 0) { /* one item: dsp is the top index, -1 = empty */
vm->error = 1;
return;
}
cell_t strategy = vm->data_stack[--vm->dsp];
cell_t strategy = vm_pop(vm);
if (strategy == 0 || strategy == 1) {
vm->lookup_strategy = (int)strategy;
}
@@ -204,12 +204,12 @@ void forth_SHOW_HEAT_OPTIMIZATION(VM *vm) {
void forth_COMPARE_LOOKUPS(VM *vm) {
if (vm->error) return;
if (vm->dsp < 1) {
if (vm->dsp < 0) { /* one item: dsp is the top index, -1 = empty */
vm->error = 1;
return;
}
cell_t iterations = vm->data_stack[--vm->dsp];
cell_t iterations = vm_pop(vm);
if (iterations <= 0) {
printf("Need positive iteration count\n");
return;
+2 -2
View File
@@ -91,13 +91,13 @@ extern size_t sf_fc_count[256];
void forth_BENCH_DICT_LOOKUP(VM *vm) {
if (vm->error) return;
if (vm->dsp < 1) {
if (vm->dsp < 0) { /* one item: dsp is the top index, -1 = empty */
vm->error = 1;
printf("BENCH-DICT-LOOKUP: Need iteration count on stack\n");
return;
}
cell_t iterations = vm->data_stack[--vm->dsp];
cell_t iterations = vm_pop(vm);
if (iterations < 1) {
printf("BENCH-DICT-LOOKUP: iterations must be >= 1\n");
return;
+2 -2
View File
@@ -251,13 +251,13 @@ void forth_PHYSICS_CALC_KNOBS(VM *vm) {
void forth_PHYSICS_BURN(VM *vm) {
if (vm->error) return;
if (vm->dsp < 1) {
if (vm->dsp < 0) { /* one item: dsp is the top index, -1 = empty */
vm->error = 1;
printf("PHYSICS-BURN: Need count on stack\n");
return;
}
cell_t burn_count = vm->data_stack[--vm->dsp];
cell_t burn_count = vm_pop(vm);
if (burn_count < 1) {
printf("PHYSICS-BURN: count must be >= 1\n");
+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);
}
/**