Scope VM fault halt to the faulting identity's own session (FABRIC-3.md §XI.4)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

A standalone WIREBIND identity (no Zuse, USE'd in directly) hitting an
ACL-denied word halted the entire machine -- Hera, Hermes, Artemis, all
of it -- instead of just that identity's own session. sk_fault_handler()
was being called unconditionally on whichever VM's ->error was set, with
no distinction between Hera's own root session (where "no fallthrough
surface" is the correct, deliberate fail-closed behavior) and a
USE'd-in guest identity (which should recover and resume at its own
prompt instead of taking the fleet down with it).

Both call sites (sk_repl_step, sk_repl_run) now compare the faulting VM
against Hera before deciding: Hera's own session still halts by design;
any other VM prints a recovery message, clears its fault state, and
continues.

Also: mint identities 01-06 with the same FORTH-79/83 restricted
personality identity 00 already had, verified via the fixed fault
scoping above (which this verification pass surfaced).

Verified live on amd64 (both the Hera-halts and identity-recovers
branches); three-arch clean qemu acceptance passed (riscv64's first
attempt hit an unrelated virtio_blk I/O timeout hang, a known QEMU/TCG
flake -- a clean retry booted normally).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
This commit is contained in:
Robert Allan James
2026-09-09 20:41:51 -04:00
co-authored by Claude Sonnet 5
parent 9bcc70647b
commit d6661b5eed
27 changed files with 72093 additions and 5 deletions
+48
View File
@@ -1852,3 +1852,51 @@ just of her ever attaching at all before this point). Both bugs happened to corr
"Zuse first" framing without being caused by it — a genuine trap for exactly the kind of "Zuse first" framing without being caused by it — a genuine trap for exactly the kind of
live-forensics-driven investigation this section is a record of, and worth naming plainly live-forensics-driven investigation this section is a record of, and worth naming plainly
rather than editing out of the historical record. rather than editing out of the historical record.
### XI.4 — Identities 0106 minted with the restricted personality; a standalone identity's
ACL denial halted the whole machine, not just that session — CLOSED
With §XI.3 fixed, all 6 remaining identity thumbdrives (`01``06`) were re-minted with the same
`MINT_PERSONALITY_STD79_LOCKDOWN` restricted personality identity `00` already had (§IX/`f4ded3e`):
each drive's devblock-1 signature was zeroed (`dd ... seek=1 count=1 conv=notrunc`, invalidating
recognition without touching the rest of the drive) and re-minted via `-1 MINT` in a single
Zuse-attached boot. All 6 confirmed via "MINT: identity minted".
Verifying identity `00` standalone (no Zuse, `USE`'d in, typing `VM-EXEC` to confirm a denied
word is refused without taking anything else down) surfaced a real bug: the denial *did* refuse
correctly, but the entire machine halted — Hera, Hermes, Artemis, everything — not just that
one identity's own session. Root cause: `sk_fault_handler()` (`repl.c`, gated by
`EMERGENCY_CONSOLE_ENABLED=0`, the production default) is genuinely correct, deliberate,
fail-closed behavior for **Hera's own** session — "no fallthrough surface" is exactly the right
call when the primary console itself faults. But both of its call sites (`sk_repl_step()`,
`sk_repl_run()`'s main loop) called it unconditionally on whichever VM's `->error` was set,
with no distinction between "this is Hera's own bare session faulting" and "this is a guest
identity's `USE`'d-in session faulting" — so a WIREBIND identity's ACL denial halted Hera right
along with itself, an ordinary FORTH-79 command mistake for a non-privileged guest taking down
the whole fleet.
Fix (`repl.c`, both call sites): compare the faulting VM against Hera specifically before
deciding — `sk_repl_step()` already had the right comparison available
(`vm == (VM *)sk_get_mama_vm()`); `sk_repl_run()`'s loop needed the same, using its own `active`
(the `USE`-redirected VM, defaulting to `vm`/Hera when nothing is redirected — see
`g_repl_active_vm`). Hera's own session still calls `sk_fault_handler()` (halts, by design) when
the comparison matches; any other VM instead prints "VM fault -- session recovered, resuming",
clears `->error`/`->halted`/`->abort_requested`, and lets the REPL loop continue — the session
recovers at its own next prompt instead of taking the machine down with it.
Live-debugging note: the first attempt to verify this fix (before this session's own
compaction) appeared to fail — the old halt message still fired. Root cause of *that*: a leaked
`qemu-system-x86_64` process from the same investigation, still running the *pre-fix* binary,
was mistaken for a fresh boot (see `feedback_check_leaked_qemu_procs_before_slow_theory.md`).
Once that stale process was killed and a genuinely fresh instance booted, both cases verified
correctly on the first try: an identity's own `USE`'d session recovers gracefully from a denied
`VM-EXEC` (console stayed fully interactive afterward, confirmed with a follow-up arithmetic
command), and Hera's own bare `zuse)ok>` session still halts correctly on a genuine fault
(confirmed with a deliberately-undefined word) — the fail-closed intent for the primary console
is preserved, only guest identity sessions are now scoped.
**Verified:** live on amd64 (both branches, as above); three-arch `clean qemu` acceptance
(amd64/aarch64/riscv64) all passed — riscv64's first attempt hung with a `virtio_blk` I/O
timeout mid-boot (unrelated to this fix, no block-subsystem code touched this session; a clean
retry booted normally, consistent with this project's known occasional QEMU/TCG flakiness, see
`project_unexplained_qemu_sigterm_20260818.md`).
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated # Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-09T18:24:33Z --> <!-- Generated by mkcapsule --manifest 2026-09-10T00:40:18Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. --> <!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live --> <!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. --> <!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
RAW SERIAL UP
[CKPT 002] Serial (COM1) initialized
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+46 -4
View File
@@ -1165,8 +1165,19 @@ int sk_repl_step(VM *vm)
/* Wired 2026-09-05: sk_fault_handler() existed but was never /* Wired 2026-09-05: sk_fault_handler() existed but was never
* called from here -- the "halts VM on error" half of this * called from here -- the "halts VM on error" half of this
* function's own doc comment was aspirational, not real, until * function's own doc comment was aspirational, not real, until
* now. Real for the headless-until-login default. */ * now. Real for the headless-until-login default.
sk_fault_handler(vm); *
* 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;
}
#endif #endif
} else { } else {
console_puts(" ok\n"); console_puts(" ok\n");
@@ -1253,8 +1264,39 @@ void sk_repl_run(VM *vm)
active->error = 0; active->error = 0;
#else #else
/* Wired 2026-09-05, same as sk_repl_step()'s matching branch /* Wired 2026-09-05, same as sk_repl_step()'s matching branch
* above -- sk_fault_handler() existed but was never called. */ * above -- sk_fault_handler() existed but was never called.
sk_fault_handler(active); *
* 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;
}
#endif #endif
} else { } else {
console_puts(" ok\n"); console_puts(" ok\n");