Stage A: fix USE's silent stack-underflow/bad-address guards, and the Hera fault-scoping gap they exposed (FABRIC-3.md §XXXII.1)
mama_word_use()'s two silent vm->error=1 guards (dsp<1 stack underflow, NULL from vm_ptr()) now print a diagnostic and return, matching the function's other five guards. Live verification of that fix alone surfaced a bigger problem: with the guard no longer silent, the REPL proceeds to interpret the leftover token as an unrecognized word, which independently sets vm->error, and sk_repl_step()/sk_repl_run()'s Hera-branch still hard-halted on that. Investigated kernel_main.c's boot/capsule-load paths directly: they already catch and clear mama->error entirely separately, before sk_repl_run() is ever entered -- so the "no fallthrough surface" halt in these two REPL functions was never protecting a boot-time fault, only an ordinary interactive REPL-turn one. Both functions now recover unconditionally on any VM's error, Hera included, matching how a redirected (WIREBIND/USE'd) identity's session already recovered. Removed the now-fully-unused sk_fault_handler(). Verified live, amd64: USE rajames at Zuse's own console prints the new diagnostic, then "VM fault -- session recovered, resuming", console stays interactive afterward. Three-arch clean qemu acceptance passed before and after the Hera-fault-scoping change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
05159c9f9e
commit
5c5896fbc1
@@ -449,7 +449,7 @@ void mama_word_use(VM *vm)
|
||||
const char *src;
|
||||
|
||||
if (vm->dsp < 1) {
|
||||
vm->error = 1;
|
||||
console_println("USE: expects S\" name\" USE -- nothing on the stack");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -464,7 +464,7 @@ void mama_word_use(VM *vm)
|
||||
/* caddr is a VM address; S" ( -- c-addr u ) stores chars directly at caddr */
|
||||
{
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!p) { vm->error = 1; return; }
|
||||
if (!p) { console_println("USE: invalid address on the stack"); return; }
|
||||
src = (const char *)p;
|
||||
}
|
||||
for (i = 0; i < (uint32_t)u; i++) name_buf[i] = src[i];
|
||||
|
||||
+45
-58
@@ -1169,16 +1169,17 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
* - Calls vm_interpret
|
||||
* - Prints " ok" or " ERROR"
|
||||
* - When EMERGENCY_CONSOLE_ENABLED=1: resets vm->error and loops (recovery)
|
||||
* - When EMERGENCY_CONSOLE_ENABLED=0: halts VM on error (no fallthrough surface)
|
||||
* - When EMERGENCY_CONSOLE_ENABLED=0: an interactive REPL-turn fault on
|
||||
* any VM (Hera included, as of FABRIC-3.md §XXXII.1) recovers the same
|
||||
* way -- prints a message, clears vm->error/halted/abort_requested, and
|
||||
* the loop continues at that VM's own next prompt. Boot-time faults
|
||||
* (capsule load, birth scripts) never reach this code at all; they are
|
||||
* caught and cleared directly in kernel_main.c before sk_repl_run() is
|
||||
* ever entered, so no "no fallthrough surface" halt is needed or given
|
||||
* up here -- the C-level `sk_fault_handler()` this comment used to
|
||||
* describe was retired the same day, having no remaining caller.
|
||||
*===========================================================================*/
|
||||
|
||||
#if !EMERGENCY_CONSOLE_ENABLED
|
||||
static void sk_fault_handler(VM *vm) {
|
||||
console_println("VM fault — emergency console disabled; halting");
|
||||
vm->halted = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================
|
||||
* sk_repl_dispatch_line - console-VM + user-VM pair relay (FABRIC-2.md
|
||||
* Phase F, 2026-08-28).
|
||||
@@ -1319,22 +1320,23 @@ int sk_repl_step(VM *vm)
|
||||
console_puts(" ERROR\n");
|
||||
vm->error = 0;
|
||||
#else
|
||||
/* Wired 2026-09-05: sk_fault_handler() existed but was never
|
||||
* called from here -- the "halts VM on error" half of this
|
||||
* function's own doc comment was aspirational, not real, until
|
||||
* now. Real for the headless-until-login default.
|
||||
*
|
||||
* FABRIC-3.md, 2026-09-09: scoped to Hera's own session only --
|
||||
* see sk_repl_run()'s matching fix (and its own doc comment) for
|
||||
* why a non-mama target must recover instead of halt here too. */
|
||||
if (vm == (VM *)sk_get_mama_vm()) {
|
||||
sk_fault_handler(vm);
|
||||
} else {
|
||||
console_println("VM fault -- session recovered, resuming");
|
||||
vm->error = 0;
|
||||
vm->halted = 0;
|
||||
vm->abort_requested = 0;
|
||||
}
|
||||
/* FABRIC-3.md §XXXII.1, 2026-09-15: an ordinary interactive
|
||||
* mistake (a typo, an unrecognized word, a stack-convention
|
||||
* violation) at Zuse's own console used to hard-halt the entire
|
||||
* kernel here, because this branch treated Hera's own session
|
||||
* as a "no fallthrough surface" emergency and every other VM's
|
||||
* session as recoverable (2026-09-09 fix, see git history). That
|
||||
* asymmetry was never actually protecting a boot-time fault --
|
||||
* boot/capsule-load errors are caught and cleared entirely
|
||||
* separately, in kernel_main.c, well before sk_repl_run() is
|
||||
* ever entered -- so by the time this function runs at all, any
|
||||
* vm->error here is by construction an interactive REPL-turn
|
||||
* fault, on any VM including Hera. Recovers unconditionally now,
|
||||
* matching every other VM's own session exactly. */
|
||||
console_println("VM fault -- session recovered, resuming");
|
||||
vm->error = 0;
|
||||
vm->halted = 0;
|
||||
vm->abort_requested = 0;
|
||||
#endif
|
||||
} else {
|
||||
console_puts(" ok\n");
|
||||
@@ -1445,40 +1447,25 @@ void sk_repl_run(VM *vm)
|
||||
console_puts(" ERROR\n");
|
||||
active->error = 0;
|
||||
#else
|
||||
/* Wired 2026-09-05, same as sk_repl_step()'s matching branch
|
||||
* above -- sk_fault_handler() existed but was never called.
|
||||
*
|
||||
* FABRIC-3.md, 2026-09-09: live-caught -- a standalone
|
||||
* WIREBIND identity (no Zuse involved at all, USE never
|
||||
* typed) hitting a denied word halted the *entire machine*,
|
||||
* not just that identity's own session. Root cause: this
|
||||
* loop's own exit condition (`while (!vm->halted)` above)
|
||||
* checks vm -- Hera, the loop's original owner -- but
|
||||
* sk_fault_handler() was being called on `active`, which
|
||||
* capsule_wirebind's own attach flow had already redirected
|
||||
* to the new identity's own VM (sk_repl_set_active_vm()) by
|
||||
* the time any command could be typed. Setting active->halted
|
||||
* left Hera's own vm->halted untouched -- the loop kept
|
||||
* running -- but every subsequent iteration kept re-selecting
|
||||
* the same now-halted, never-recovering `active`, producing
|
||||
* no further output and no further progress: a de facto full
|
||||
* freeze despite Hera's own loop technically still spinning.
|
||||
* Captain Bob, 2026-09-09: "scope the halt to just that
|
||||
* identity's session gracefully and restart the session or
|
||||
* resume it." Hera's own direct session (active == vm) keeps
|
||||
* the strict "no fallthrough surface" halt -- a fault in the
|
||||
* root console is a genuine full-system emergency -- but any
|
||||
* redirected (WIREBIND/USE'd) identity's own fault now
|
||||
* recovers instead: clear the fault state and let that same
|
||||
* identity keep going at its own prompt next iteration. */
|
||||
if (active == vm) {
|
||||
sk_fault_handler(active);
|
||||
} else {
|
||||
console_println("VM fault -- session recovered, resuming");
|
||||
active->error = 0;
|
||||
active->halted = 0;
|
||||
active->abort_requested = 0;
|
||||
}
|
||||
/* FABRIC-3.md §XI.4 (2026-09-09) scoped this recovery to any
|
||||
* *redirected* (WIREBIND/USE'd) identity, keeping Hera's own
|
||||
* direct session (active == vm) on the strict "no fallthrough
|
||||
* surface" halt -- reasoned at the time as protecting against
|
||||
* a genuine full-system emergency. §XXXII.1 (2026-09-15) found
|
||||
* that reasoning didn't hold: boot/capsule-load errors are
|
||||
* caught and cleared entirely separately in kernel_main.c,
|
||||
* before this loop is ever entered, so any active->error seen
|
||||
* here -- Hera's own session included -- is by construction
|
||||
* an ordinary interactive REPL-turn fault (a typo, an
|
||||
* unrecognized word), not a boot-time catastrophe. An
|
||||
* unqualified typo at Zuse's own console was hard-halting the
|
||||
* entire kernel as a direct result of this asymmetry. Recovers
|
||||
* unconditionally now, matching every redirected identity's
|
||||
* own session exactly -- no special case for `active == vm`. */
|
||||
console_println("VM fault -- session recovered, resuming");
|
||||
active->error = 0;
|
||||
active->halted = 0;
|
||||
active->abort_requested = 0;
|
||||
#endif
|
||||
} else {
|
||||
console_puts(" ok\n");
|
||||
|
||||
Reference in New Issue
Block a user