Four bugs found live verifying the 8 identity thumbdrives (FABRIC-3.md §IX)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

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:
Robert Allan James
2026-09-06 01:49:13 -04:00
co-authored by Claude Sonnet 5
parent 0bae928aad
commit 2c1b3cd695
23 changed files with 45486 additions and 124 deletions
+13
View File
@@ -43,6 +43,19 @@ typedef enum {
* user_identity_seed_t's fixed fields, or
* email/phone too long (both may be NULL/empty
* -- that's "null", not invalid). */
MINT_ERR_VERIFY_FAILED, /* every devblock write reported success, but a
* post-write read-back (2026-09-06) found the
* drive doesn't actually read back as a valid,
* complete home-blocks identity -- caught
* live: a device that enumerates and accepts
* writes can still fail to read back correctly
* under real hardware/emulation conditions
* (e.g. concurrent multi-device USB load), and
* blkio_write() returning BLK_OK is not by
* itself proof the bytes landed. The drive may
* be left partially or incorrectly minted --
* treat identically to MINT_ERR_WRITE_FAIL for
* retry purposes. */
} MintResult;
/**
+9 -1
View File
@@ -109,8 +109,16 @@ int capsule_wirebind_eject(void);
* since the last flush is lost, which is correct unclean-removal
* semantics. Otherwise identical to capsule_wirebind_eject(): same
* active-VM reset-before-kill step, same tracked-state clear.
*
* FABRIC-3.md §VII follow-on, 2026-09-06: now requires the departing
* device to actually be the one tracked as this WIREBIND user's own
* (g_wirebind_attached_dev) -- a real bug otherwise, found live once
* genuine multi-device attach made a *different* device's detach
* reachable while a WIREBIND user's own stayed attached.
*
* @param dev The device that just detached; every other value is a no-op.
*/
void capsule_wirebind_unclean_detach(void);
void capsule_wirebind_unclean_detach(struct blkio_dev *dev);
/**
* capsule_wirebind_attached_username - The plain username (no "~user"
+26 -8
View File
@@ -68,17 +68,35 @@ void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
* one function covers both the graceful (EJECT) and abrupt (hot-unplug)
* call sites identically.
*
* No-op if the currently-tracked attached device isn't Zuse's own
* (nothing to do -- some other identity's drive is what's leaving, or
* nothing is attached at all). Clears mama_vm->zuse_session only --
* zuse_cert_installed and the cert itself stay put, permanently, per
* vm_zuse_cert_install()'s own one-way design; re-attaching her own
* drive re-authenticates via capsule_zuse_boot_try_attach() without
* re-minting anything.
* No-op if `dev` isn't the device currently tracked as Zuse's own (nothing
* to do -- some other identity's drive is what's leaving, or nothing is
* attached at all) -- FABRIC-3.md §VII follow-on, 2026-09-06: this doc
* comment always claimed that no-op, but the check itself was missing
* until now (the function took no device parameter at all) -- confirmed
* live as a real bug once genuine multi-device attach made it reachable
* (detaching an unrelated device logged Zuse out too). Clears
* mama_vm->zuse_session only -- zuse_cert_installed and the cert itself
* stay put, permanently, per vm_zuse_cert_install()'s own one-way design;
* re-attaching her own drive re-authenticates via
* capsule_zuse_boot_try_attach() without re-minting anything.
*
* @param mama_vm Hera's own VM (zuse_session lives here).
* @param dev The device that just detached -- compared against the one
* tracked as hers; every other value is a no-op.
*/
void capsule_zuse_boot_logout(VM *mama_vm);
void capsule_zuse_boot_logout(VM *mama_vm, struct blkio_dev *dev);
/**
* capsule_zuse_boot_attached_dev - The device currently tracked as Zuse's
* own, or NULL if she isn't attached this boot. FABRIC-3.md §VII follow-on,
* 2026-09-06: exists so an explicit, operator-initiated logout (EJECT,
* mama_forth_words.c) can pass her own device back into
* capsule_zuse_boot_logout() without needing to already know it -- unlike
* the abrupt hot-unplug path, EJECT isn't reacting to any specific
* device's detach event, so there is no other device value available at
* that call site to check against.
*/
struct blkio_dev *capsule_zuse_boot_attached_dev(void);
#endif /* __STARKERNEL__ */
+25 -31
View File
@@ -131,46 +131,40 @@ int sk_console_key_available(void);
* prompt line is their own (shim.c's fgets(), i.e.
* QUERY/EXPECT/ACCEPT) pass 0 so "ok> " never gets stamped
* onto their mid-word input context.
* @return number of characters placed in buf, not counting the NUL
* @return number of characters placed in buf, not counting the NUL, or -1
* (2026-09-06) when reanchor_prompt is nonzero and the attached
* identity logged out while this call was blocked waiting for
* input with nothing typed yet -- see repl.c's own doc comment
* on this function for what a caller must do with -1.
*/
int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt);
/**
* sk_console_mark_login - Record that a real identity has logged in via an
* attached thumbdrive, decided in conversation 2026-09-05: no console for
* the running system unless a thumbdrive is present -- headless until the
* first successful login, regardless of which path performs it (a regular
* user's WIREBIND console-VM birth, capsule_wirebind.c, or Zuse's own
* attach/genesis-mint, capsule_zuse_boot.c). Both call this on their own
* success path; neither is treated as special here, per direct instruction
* ("nothing special about zuse as a user except zuse has no ACLs") --
* this is one shared signal, not a Zuse-specific carve-out. Idempotent
* (a second login, e.g. a second WIREBIND user later, is a harmless no-op).
*/
void sk_console_mark_login(void);
/**
* sk_console_login_occurred - Whether sk_console_mark_login() has ever
* been called this boot. Read by sk_repl_headless_wait()'s own exit
* condition; exposed publicly for anything else that needs to know
* whether the console is unlocked yet.
*/
int sk_console_login_occurred(void);
/**
* sk_repl_headless_wait - Idle-service loop with no interactive surface
* at all: no banner, no prompt, no console_getc()/readline. Runs
* heartbeat_service() and the same SK_IDLE_BEAT_INTERVAL-gated
* sk_repl_idle(mama) cadence sk_console_readline()'s own idle branch
* uses -- so USB/WIREBIND/Zuse-attach detection, the heartbeat, and all
* other idle-tick subsystems keep running -- until sk_console_login_
* occurred() becomes true, at which point it returns. Called from
* kernel_main.c in place of an immediate sk_repl(mama) call when
* EMERGENCY_CONSOLE_ENABLED is off (the new default, 2026-09-05): no
* thumbdrive, no prompt, per direct instruction. When
* EMERGENCY_CONSOLE_ENABLED is on (the debug/recovery escape hatch),
* kernel_main.c skips this and calls sk_repl(mama) immediately instead,
* exactly as before this change.
* other idle-tick subsystems keep running -- until a real identity is
* currently attached (Zuse's own session, or a WIREBIND user), at which
* point it returns.
*
* Revised 2026-09-06: originally exited on a one-way sticky "has anyone
* ever logged in this boot" flag (sk_console_mark_login()/sk_console_
* login_occurred(), both retired) -- that let a real gap through, found
* live: once the flag tripped once, it never reset, so a later full
* logout (nobody attached at all) fell through to a bare, unauthenticated
* prompt instead of going silent again. This now checks live attach
* state instead (repl.c's own sk_console_identity_present()), and is
* called from two places: once from kernel_main.c in place of an
* immediate sk_repl(mama) call when EMERGENCY_CONSOLE_ENABLED is off (the
* default, 2026-09-05) -- no thumbdrive, no prompt, at boot -- and again
* from inside sk_repl_run()'s own main loop, every time nobody is
* currently attached, so the same silence re-engages after any later
* logout mid-boot too. When EMERGENCY_CONSOLE_ENABLED is on (the debug/
* recovery escape hatch), neither call site applies -- the console shows
* immediately and stays visible regardless of attach state, exactly as
* before this change.
*
* @param mama Hera's own VM instance -- the idle-dispatch target,
* same as every other sk_repl_idle() caller uses.