Four bugs found live verifying the 8 identity thumbdrives (FABRIC-3.md §IX)
All found by actually running the identity workflow §VII/§VIII made possible, not by code review: 1. Zuse/WIREBIND cross-contamination on detach: capsule_zuse_boot_logout() and capsule_wirebind_unclean_detach() both had no device parameter, so an unrelated device detaching (while the real owner's own stayed attached) incorrectly tore down the wrong session. Both now compare the departing device against their own tracked one, mirroring capsule_wirebind.c's pre-existing g_wirebind_attached_dev precedent. 2. Dictionary-entry memory leak: vm_create_word()'s sf_malloc()'d DictEntry (plus a second per-entry allocation for transition_metrics) was never freed by vm_cleanup(), in both the hosted and kernel implementations. Caused a real kernel PANIC after 8-9 repeated VM birth/kill cycles in one boot. Fixed by walking vm->latest in both. 3. sf_malloc/sf_free (alloc_kernel.c) was a 4MB bump arena with a deliberate no-op free, sized on "VM born once, never killed" -- fix #2 alone didn't stop the panic because free() itself discarded the pointer regardless. Given a real free list (first-fit reuse). 4. Headless-console gate didn't re-engage after a mid-boot logout: the original fix (sk_console_mark_login(), one-way sticky) only gated the first login of the boot. Replaced with a live check (sk_console_identity_present()) re-evaluated continuously, including inside sk_console_readline()'s own blocking idle loop -- the console is normally sitting blocked there when a hot-unplug logout happens, so checking only at the top of the REPL loop wasn't enough. Also: MINT now verifies its own write (verify_mint(), capsule_mint.c) by reading back through the same check a real attach performs, rather than trusting blkio_write()'s BLK_OK alone -- logged via log_message(), not console_println(), per direct instruction. Verified live, amd64: the full 8-identity repeated attach/detach cycle that previously panicked at the same point every time now completes clean, and a full serial-log sweep found zero bare unauthenticated prompts anywhere in the run. Three-arch clean-qemu acceptance passed. Still open, not fixed here: a 3+-simultaneous-device USB enumeration failure found in a separate live test, not yet root-caused. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0bae928aad
commit
2c1b3cd695
+99
-19
@@ -99,16 +99,26 @@ VM *sk_repl_get_active_vm(void) { return g_repl_active_vm; }
|
||||
|
||||
/*===========================================================================
|
||||
* Headless-until-login gate, decided 2026-09-05: no console for the
|
||||
* running system unless a thumbdrive is present. One shared flag, set by
|
||||
* either login path (capsule_wirebind.c's regular-user console-VM birth,
|
||||
* capsule_zuse_boot.c's own attach/genesis-mint) -- neither is special,
|
||||
* per direct instruction. See repl.h's own doc comments.
|
||||
*===========================================================================*/
|
||||
|
||||
static int g_console_login_occurred = 0;
|
||||
|
||||
void sk_console_mark_login(void) { g_console_login_occurred = 1; }
|
||||
int sk_console_login_occurred(void) { return g_console_login_occurred; }
|
||||
* running system unless a thumbdrive is present.
|
||||
*
|
||||
* Revised 2026-09-06: this was originally a one-way sticky flag
|
||||
* (sk_console_mark_login(), set once by either login path and never
|
||||
* cleared), gating only the very first entry into sk_repl_run() at boot.
|
||||
* That let a real security gap through, found live during this session's
|
||||
* own repeated identity-verification workflow: once anyone logged in even
|
||||
* once, the console stayed visible for the rest of the boot -- a later
|
||||
* full logout (nobody attached at all) fell through to a bare,
|
||||
* unauthenticated "ok>" instead of going silent again. sk_console_
|
||||
* identity_present() replaces the sticky flag with a live check (mirrors
|
||||
* sk_print_prompt()'s own zuse_session/WIREBIND-username check exactly),
|
||||
* and sk_repl_run()'s own main loop now re-checks it every iteration, not
|
||||
* just once before the loop starts -- see its own call site below. */
|
||||
static int sk_console_identity_present(void) {
|
||||
VM *mama_vm = (VM *)sk_get_mama_vm();
|
||||
if (mama_vm && mama_vm->zuse_session) return 1;
|
||||
if (capsule_wirebind_attached_username() != (const char *)0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*===========================================================================
|
||||
* Currently attached home-blocks device: mirrors g_repl_active_vm's own
|
||||
@@ -377,13 +387,18 @@ static void sk_repl_idle(VM *active_vm)
|
||||
/* FABRIC-2.md §F.10 decision 2 (UNCLEAN, closed alongside EJECT):
|
||||
* the device is already gone -- no-op if WIREBIND never had
|
||||
* anything tracked (general-purpose USB use, not a home-blocks
|
||||
* identity drive). */
|
||||
capsule_wirebind_unclean_detach();
|
||||
* identity drive), OR if the device that left wasn't the one
|
||||
* WIREBIND tracks (FABRIC-3.md §VII follow-on, 2026-09-06 --
|
||||
* genuine multi-device attach means it might be a different
|
||||
* device leaving while a WIREBIND user's own stays attached). */
|
||||
capsule_wirebind_unclean_detach(usb_blk_dev);
|
||||
|
||||
/* FABRIC-2.md §I.8, re-scoped 2026-09-04: Zuse logs out on device
|
||||
* removal exactly like a WIREBIND user -- no-op if the device
|
||||
* that just left wasn't hers. */
|
||||
capsule_zuse_boot_logout((VM *)sk_get_mama_vm());
|
||||
* that just left wasn't hers (FABRIC-3.md §VII follow-on,
|
||||
* 2026-09-06: that no-op is now real, see capsule_zuse_boot_
|
||||
* logout()'s own updated doc comment). */
|
||||
capsule_zuse_boot_logout((VM *)sk_get_mama_vm(), usb_blk_dev);
|
||||
}
|
||||
|
||||
/* FABRIC-0.md/FABRIC-1.md Section V item 6: "a cheap 'anything dirty?
|
||||
@@ -630,11 +645,15 @@ int sk_console_getkey(VM *active_vm)
|
||||
* shape as sk_console_getkey() above, minus the key-reading entirely: no
|
||||
* banner, no prompt, no console_getc()/readline of any kind -- this is
|
||||
* exactly the "no console for the running system unless a thumbdrive is
|
||||
* present" boundary, decided 2026-09-05. Exits the moment
|
||||
* sk_console_login_occurred() becomes true. */
|
||||
* present" boundary, decided 2026-09-05. Exits the moment sk_console_
|
||||
* identity_present() becomes true -- called both once at boot
|
||||
* (kernel_main.c, before the first ever login) and again from inside
|
||||
* sk_repl_run()'s own main loop whenever the last attached identity logs
|
||||
* out mid-boot (2026-09-06 revision, see sk_console_identity_present()'s
|
||||
* own doc comment for why the boot-only version wasn't enough). */
|
||||
void sk_repl_headless_wait(VM *mama)
|
||||
{
|
||||
while (!sk_console_login_occurred()) {
|
||||
while (!sk_console_identity_present()) {
|
||||
heartbeat_service();
|
||||
uint64_t now = heartbeat_ticks();
|
||||
if (now - g_last_beat_tick >= SK_IDLE_BEAT_INTERVAL) {
|
||||
@@ -664,7 +683,17 @@ int sk_console_key_available(void)
|
||||
* Non-blocking poll of console_getc(). While no character is ready the idle
|
||||
* spin services the adaptive heartbeat at SK_IDLE_BEAT_INTERVAL tick cadence.
|
||||
* Supports backspace (0x7F and \b) and ignores other control characters.
|
||||
* Returns the number of characters placed in buf (not counting '\0').
|
||||
* Returns the number of characters placed in buf (not counting '\0'), or
|
||||
* -1 (2026-09-06) when called with reanchor_prompt nonzero and the
|
||||
* identity that was attached when the caller's prompt was printed logs
|
||||
* out while this call is still blocked waiting for input with nothing yet
|
||||
* typed (n == 0) -- callers with reanchor_prompt nonzero (the REPL's own
|
||||
* top-level prompt sites) must check for this and route back to
|
||||
* sk_repl_headless_wait() rather than treating it as an empty line; buf
|
||||
* is left as an empty string in this case too, matching a real empty
|
||||
* line, so a caller that doesn't check the return value degrades to the
|
||||
* pre-fix behavior (an extra harmless " ok") rather than misbehaving.
|
||||
* shim.c's fgets() (reanchor_prompt == 0) never receives -1.
|
||||
*
|
||||
* Public (declared in repl.h): shim.c's fgets()/QUERY's own real body call
|
||||
* this directly -- same line-editing behavior for a mid-word EXPECT/QUERY as
|
||||
@@ -766,6 +795,27 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
* unchanged, so the final state after the chatter dies down is
|
||||
* a fresh prompt on the last visible line, cursor on it.
|
||||
*/
|
||||
/* Headless-until-login gate, 2026-09-06: the identity that was
|
||||
* attached when the caller printed its prompt (sk_print_prompt(),
|
||||
* reflected in reanchor_prompt callers only -- shim.c's fgets()
|
||||
* passes 0 and is unaffected) may have logged out while we sat
|
||||
* here blocked waiting for input -- WIREBIND EJECT/unclean
|
||||
* detach, or Zuse's own logout, both reachable from
|
||||
* sk_repl_idle() just above. Re-printing the prompt in that
|
||||
* case (the block below) would just show a *correct* bare
|
||||
* "ok>" -- true to current state, but still an unauthenticated
|
||||
* interactive surface sitting on screen, which the headless-
|
||||
* until-login design (Kconfig.heartbeat's EMERGENCY_CONSOLE_
|
||||
* ENABLED) exists specifically to prevent. Bail out instead so
|
||||
* the caller (sk_repl_run()'s own main loop) can drop back into
|
||||
* sk_repl_headless_wait() -- confirmed live as a real gap
|
||||
* before this fix (a bare, unauthenticated prompt stayed on
|
||||
* screen after every logout for the rest of the boot). n == 0
|
||||
* only: never abandon a line the user is actively typing. */
|
||||
if (reanchor_prompt && n == 0 && !sk_console_identity_present()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reanchor_prompt && n == 0 &&
|
||||
console_tx_count() != prompt_tx_mark)
|
||||
{
|
||||
@@ -1005,6 +1055,22 @@ void sk_repl_run(VM *vm)
|
||||
vm->halted = 0;
|
||||
|
||||
while (!vm->halted) {
|
||||
#if !EMERGENCY_CONSOLE_ENABLED
|
||||
/* Headless-until-login gate, revised 2026-09-06: re-checked every
|
||||
* iteration, not just once before this loop starts (kernel_main.c's
|
||||
* own sk_repl_headless_wait() call, still in place, only covers the
|
||||
* very first login of the boot). Whoever was attached may have
|
||||
* logged out since the last iteration (WIREBIND EJECT/unclean
|
||||
* detach, Zuse's own logout) -- if nobody is attached right now,
|
||||
* go back to silent waiting instead of falling through to a bare,
|
||||
* unauthenticated prompt. See sk_console_identity_present()'s own
|
||||
* doc comment for the live bug this closes. */
|
||||
if (!sk_console_identity_present()) {
|
||||
sk_repl_headless_wait(vm);
|
||||
if (vm->halted) break;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* USE may redirect input to a different VM each iteration */
|
||||
active = g_repl_active_vm ? g_repl_active_vm : vm;
|
||||
|
||||
@@ -1017,7 +1083,21 @@ void sk_repl_run(VM *vm)
|
||||
* sk_print_prompt() extends this with a "(user)" segment. */
|
||||
sk_print_prompt();
|
||||
|
||||
sk_console_readline(input, sizeof(input), active, 1);
|
||||
int n = sk_console_readline(input, sizeof(input), active, 1);
|
||||
|
||||
#if !EMERGENCY_CONSOLE_ENABLED
|
||||
/* n < 0: sk_console_readline() bailed out because the identity
|
||||
* that was attached when this prompt was printed logged out
|
||||
* while we were still blocked waiting for input (2026-09-06 --
|
||||
* see sk_console_readline()'s own doc comment on this return
|
||||
* value). No " ok" here -- nothing was typed, nothing ran --
|
||||
* just loop back to the top, where the check above re-enters
|
||||
* headless silence immediately instead of showing yet another
|
||||
* prompt first. */
|
||||
if (n < 0) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (input[0] == '\0') {
|
||||
console_puts(" ok\n");
|
||||
|
||||
Reference in New Issue
Block a user