word_source: repair DECAY-RATE@ overflow guard and remove dead shadowed registrations

DECAY-RATE@ (physics_freeze_words.c) pushed to the data stack with no
capacity check and no prior pop to make room, unlike its neighbors in
the same file -- the one live, unconditional missing-guard bug the
Isabelle sweep's ~15 candidate findings reduced to once checked against
vm_push()'s real internal bounds check (see proof/FINDINGS.md SS2).

Removed dictionary_manipulation_words.c's [ ] STATE and defining_words.c's
DEFER IS DEFER@ (plus the now-orphaned defining_runtime_defer helper) --
all confirmed permanently shadowed by later dictionary registrations
(defining_words.c and defer_words.c respectively), per FORTH's
newest-first lookup. No behavior change: the removed code was already
unreachable.

Verified: hosted `make` builds clean under -Wall -Werror; the hosted
self-test suite passes 965/965 implemented tests with no regressions.
Three-architecture QEMU acceptance boot, all clean to ok> with an
identical dict_hash=0x24b4279f0670aa3a across amd64/aarch64/riscv64 and
identical 1003/965/0/0 test totals -- logs attached.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-14 21:33:17 -04:00
co-authored by Claude Sonnet 5
parent 3426d6a4a7
commit dfdabcc2d7
13 changed files with 25773 additions and 170 deletions
+7 -2
View File
@@ -14,8 +14,13 @@ begin
`STATE` (Module 17, line 126). FORTH dictionary lookup finds the MOST
RECENT definition of a name first, so defining_words.c's versions are
the only ones ever reachable -- dictionary_manipulation_words.c's `[`/
`]`/`STATE`/`INTERPRET` are permanently shadowed, dead code from the
moment boot registration completes.
`]`/`STATE` are permanently shadowed, dead code from the moment boot
registration completes. CORRECTED 2026-08-14: `INTERPRET` is NOT part of
this shadow -- defining_words.c never registers a word named INTERPRET
at all (confirmed by grep), so dictionary_manipulation_words.c's
INTERPRET is the only registration that exists and is live, not dead.
The earlier version of this note (and proof/FINDINGS.md) incorrectly
folded it into the shadowed group.
This also CORRECTS that file's finding, not just supersedes it: its
`[`/`]`/`STATE` write/read a dead file-scope `static cell_t
@@ -37,16 +37,28 @@ begin
(include/vm.h:431, the real per-VM STATE field actively used by
vm.c/vm_core.c/vm_bootstrap.c/capsule_loader.c on both hosted and
kernel builds). This static is a single process-wide variable, not
even per-VM: in the Tripod multi-VM fleet, EVERY VM's `[`/`]`/
`INTERPRET` mutates the SAME shared cell, and `STATE` pushes ITS
address (a bare host pointer, not a VM address) -- identical across
every VM instance. `vm->state_var` itself is correctly toggled
elsewhere in the real interpreter loop, so the dictionary-space
compile/interpret bookkeeping isn't broken, but these four FORTH
words are effectively wired to a dead, cross-VM-shared shadow
variable instead of the VM's own state. Modelled below using only
`vm_mode` (which `[`/`]` DO correctly set) -- the dead static write
has no vm_state counterpart and is simply omitted, not "fixed".
even per-VM. CORRECTED 2026-08-14 -- these four words split into two
different reachability classes, not one:
- `[`, `]`, `STATE` are permanently SHADOWED (see StarForth_Defining_
Words.thy's duplicate-registration finding) -- dead code, this
static's misuse never executes for any live VM.
- `INTERPRET` is NOT shadowed (defining_words.c registers no word by
that name at all) -- it IS live and reachable, and its body really
does execute `state_variable = 0;` on every call
(dictionary_manipulation_words.c:392) alongside the correct
`vm->mode = MODE_INTERPRET`. This write is functionally inert
(nothing on any live path reads `state_variable`, since the only
word that ever read it -- this file's own `STATE` -- is itself one
of the shadowed three above), but it is live, executing code in a
registered, tested word, not dead code to remove casually per
.claude/CLAUDE.md's "never modify a registered, tested word"
guidance. Reported, not touched.
`vm->state_var` itself is correctly toggled elsewhere in the real
interpreter loop regardless, so the dictionary-space compile/
interpret bookkeeping isn't broken by any of this. Modelled below
using only `vm_mode` (which `[`/`]` DO correctly set) -- the dead
static write has no vm_state counterpart and is simply omitted, not
"fixed".
2. `dictionary_m_word_hidden`'s `#else` fallback branch (taken only if
`WORD_HIDDEN` is undefined -- it is always defined per include/vm.h:
181, so this branch is dead in every build configuration seen) calls
+12 -8
View File
@@ -22,13 +22,14 @@ begin
accessors with no vm_state counterpart, the same class of gap as every
other hardware-boundary word in this console-fabric group.
── Finding: FB-WIDTH/FB-HEIGHT have no overflow guard ──────────────────
Neither checks `ds_full` (or any capacity condition) before pushing --
same hazard class as physics_freeze_words.c's DECAY-RATE@, the first
instance of this sweep finding a missing overflow guard. Second
instance now, both in diagnostic/hardware-boundary words that read as
"just returns a number" and evidently didn't get the same underflow-
guard-writing discipline as stack-manipulation words.
── CORRECTED finding: FB-WIDTH/FB-HEIGHT are NOT missing a guard ───────
Both push via C's `vm_push()` (src/stack_management.c:75), which bounds-
checks internally (`if (vm->dsp >= STACK_SIZE - 1) ...`) before every
write -- this file's earlier claim that neither checks capacity was a
gap in this theory's abstract push model, not a real defect in the C.
Re-verified 2026-08-14 during the sweep's repair pass; see
proof/FINDINGS.md §2 for the full correction across all files this
pattern was raised against.
======================================================================== *)
(* ── PLOT ( x y color -- ) : hosted build ─────────────────────────────── *)
@@ -67,7 +68,10 @@ lemma plot_kernel_not_modelled: True
by simp
(* ── FB-WIDTH / FB-HEIGHT ( -- n ) : hosted build ─────────────────────── *)
(* C: hosted branch pushes 0 unconditionally, no capacity guard at all. *)
(* C: hosted branch pushes 0 via vm_push(), which bounds-checks internally
(see corrected header finding above) -- the abstract push below still
models an unconditional push since this theory's stack has no depth
bound to violate, but the real C is guarded. *)
definition forth_fb_width_hosted :: "vm_state \<Rightarrow> vm_state" where
"forth_fb_width_hosted vm = vm\<lparr>data_stack := 0 # data_stack vm\<rparr>"
+9 -5
View File
@@ -144,8 +144,12 @@ lemma infer_early_exit_fetch_some_false:
using assms by (simp add: forth_infer_early_exit_fetch_def)
lemma infer_fetch_words_no_overflow_guard: True
\<comment> \<open>All five push unconditionally, no ds_full check -- another instance
of the recurring missing-overflow-guard pattern.\<close>
\<comment> \<open>CORRECTED 2026-08-14: all five push via VM_PUSH (include/vm.h:706-719),
which resolves to the bounds-checked vm_push() in every build except one
defining STARFORTH_PERFORMANCE -- confirmed by repo-wide grep to never be
defined by any Makefile/Kconfig target here. Real but dormant hazard, not
a live per-word bug; same finding as StarForth_Q48_Words.thy's header.
See proof/FINDINGS.md \<section>2.\<close>
by simp
(* ── Q.VARIANCE / INFER-DECAY-SLOPE / INFER-WINDOW-WIDTH
@@ -233,15 +237,15 @@ lemma l8_table_force_target_not_modelled: True
(* ── Everything else -- NOT MODELLED ──────────────────────────────────── *)
lemma window_diversity_not_modelled: True \<comment> \<open>WINDOW-DIVERSITY: rolling_window_measure_diversity computes a fresh value from rw_history (a modelled field, but the diversity ALGORITHM over it is not) -- distinct from the already-stored rw_last_diversity. No overflow guard either.\<close>
lemma window_diversity_not_modelled: True \<comment> \<open>WINDOW-DIVERSITY: rolling_window_measure_diversity computes a fresh value from rw_history (a modelled field, but the diversity ALGORITHM over it is not) -- distinct from the already-stored rw_last_diversity. Push is via VM_PUSH -- see corrected header note near infer_fetch_words_no_overflow_guard.\<close>
by simp
lemma infer_run_not_modelled: True \<comment> \<open>INFER-RUN: allocates last_inference_outputs on first call, walks the dictionary read-only for heat stats, then runs inference_engine_run -- a whole-subsystem algorithm. This is the ONLY word that writes `last_inference`, so no fetch-after-INFER-RUN lemma can be stated.\<close>
by simp
lemma l8_mode_not_modelled: True \<comment> \<open>L8-MODE: reads vm->ssm_l8_state's legacy 16-mode int -- see file header's representation-mismatch finding. No overflow guard.\<close>
lemma l8_mode_not_modelled: True \<comment> \<open>L8-MODE: reads vm->ssm_l8_state's legacy 16-mode int -- see file header's representation-mismatch finding. Push is via VM_PUSH -- see corrected header note near infer_fetch_words_no_overflow_guard.\<close>
by simp
lemma l8_apply_not_modelled: True \<comment> \<open>L8-APPLY: no-op if !l8||!cfg, else ssm_apply_mode -- targets the same unmodelled legacy L8 state as L8-UPDATE.\<close>
by simp
lemma bayes_cache_mean_not_modelled: True \<comment> \<open>BAYES-CACHE-MEAN: hotwords_posterior_cache_hits over the unmodelled hot-words cache subsystem (StarForth_Physics_Benchmark_Words.thy). No overflow guard.\<close>
lemma bayes_cache_mean_not_modelled: True \<comment> \<open>BAYES-CACHE-MEAN: hotwords_posterior_cache_hits over the unmodelled hot-words cache subsystem (StarForth_Physics_Benchmark_Words.thy). Push is via VM_PUSH -- see corrected header note near infer_fetch_words_no_overflow_guard.\<close>
by simp
lemma bayes_cache_lower_not_modelled: True by simp
lemma bayes_cache_upper_not_modelled: True by simp
+5 -5
View File
@@ -18,11 +18,11 @@ begin
── Scope ─────────────────────────────────────────────────────────────
The fallback (non-kernel-or-wrong-arch) behaviour of all six words is
fully modelled: every one reduces to a fixed constant push (or, for
ALT+TAB, a true no-op) with NO capacity guard before pushing -- the
third and fourth instances of the missing-overflow-guard finding first
raised in physics_freeze_words.c's DECAY-RATE@ (KBD-SCAN/VKBD-EVENT/
VKBD-DEBUG/KEY-EVENT push 1-2 cells unconditionally; KBD-DEBUG pushes
2). The real (matching kernel+arch) hardware-polling bodies are NOT
ALT+TAB, a true no-op). CORRECTED 2026-08-14: all six push via C's
`vm_push()` (src/stack_management.c:75), which bounds-checks internally
-- the earlier "no capacity guard" claim here was a gap in this theory's
abstract push model, not a real defect in the C; see proof/FINDINGS.md
§2. The real (matching kernel+arch) hardware-polling bodies are NOT
modelled: i8042_pop_scancode/virtio_input_pop_event/
console_fb_toggle_graphics are all raw hardware/interrupt-state reads
with no vm_state counterpart, the same class of gap as every other
+9 -8
View File
@@ -29,13 +29,13 @@ begin
on vm_find_word + vm_compile_word + vm_allot -- several already-flagged
gaps compounded in one helper).
── Finding: three more push-only words with no overflow guard ─────────
LOG-ERROR/WARN/INFO/TEST/DEBUG and LOG-LEVEL@ push unconditionally with
no `ds_full` check -- the pattern first found at DECAY-RATE@
(physics_freeze_words.c) and repeated across framebuffer_words.c/
keyboard_words.c keeps recurring specifically in "just returns a
constant/global" words; worth citing log_words.c as further evidence
when this goes to Bob as an aggregated pattern rather than one-offs.
── CORRECTED finding: LOG-* words are NOT missing a guard ─────────────
LOG-ERROR/WARN/INFO/TEST/DEBUG and LOG-LEVEL@ all push via C's
`vm_push()` (src/stack_management.c:75), which bounds-checks internally
-- this file's earlier claim of a missing `ds_full` check was a gap in
this theory's abstract push model, not a real defect in the C.
Re-verified 2026-08-14; see proof/FINDINGS.md §2 for the full
correction across every file this pattern was raised against.
======================================================================== *)
definition LOG_ERROR_LEVEL :: cell where "LOG_ERROR_LEVEL = 0"
@@ -120,7 +120,8 @@ lemma log_level_store_target_not_modelled: True
lemma log_level_fetch_not_modelled: True
\<comment> \<open>Pushes log_get_level(), reading the same unmodelled global
LOG-LEVEL! writes to. No overflow guard either -- see file header.\<close>
LOG-LEVEL! writes to. Push is via vm_push(), which bounds-checks --
see corrected file header.\<close>
by simp
(* ── (do-log-N) runtime words -- NOT MODELLED ─────────────────────────── *)
+6 -1
View File
@@ -28,7 +28,12 @@ begin
`STARFORTH_PERFORMANCE`) path, consistent with this suite's general
assumption that stack words check bounds; the fast-path variant is
flagged, not modelled, since "no check at all" has no useful lemma to
state beyond "anything can happen."
state beyond "anything can happen." CONFIRMED 2026-08-14: repo-wide grep
shows `STARFORTH_PERFORMANCE` is never defined by any Makefile or
Kconfig target in this repo -- only referenced inside vm.h itself and
stack_words.c. So under every configuration this repo currently builds,
this file's words are on the safe path; the hazard is real but dormant,
contingent on a build flag nothing sets today.
── Q48 operations reused from StarForth_Q48_16.thy ─────────────────────
q48_add/q48_sub/q48_mul/q48_div/q48_from_u64/q48_to_u64 were already