fix(security): no VM but Artemis and Hera can make Hera run text; a message runs once; word-ACL elevation removed

Any identity could have arbitrary FORTH run as Zuse: KH-ELEVATE-SEND and
KH-BLK-ATTACH-SEND sent any text to Hera, from any VM (shown live).  And the
drain popped a message after running it, so a payload calling a colon word
ran twice.  Both fixed test first (FABRIC-3 SXXXVII):

- KH-BLK-ATTACH-SEND is ( dev-addr ok? -- sent? ), Artemis only; the kernel
  writes the reply text.  BLK-ATTACH-ACK is Hera's only.
- The drain pops before it runs.
- Elevation, ruled not needed: KH-ELEVATE-SEND, message type 8,
  zuse-eligibility.4th, ZUSE-ELIGIBLE?/ZUSE-ELIGIBILITY-ADD/
  ELEVATE-PUBKEY-UNPACK and the eligibility list removed; its disk area
  stays reserved, unused.  FABRIC-3.7 closed.

Three ISAs: POST 1052 passed; new parity baseline word_count=523
hash=0xb73b58052d591638 (deliberate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 11:38:23 -04:00
co-authored by Claude Opus 5.5
parent 029a8ed9cf
commit 5e26fad30d
35 changed files with 72785 additions and 659 deletions
+10 -14
View File
@@ -63,10 +63,10 @@
* "top-of-device, outside the user-addressable LBN pool" region
* block_subsystem.c's own meta_fence_blocks reservation (128 devblocks by
* default, BLK_META_FENCE_INIT) already carves out for exactly this kind
* of system metadata, and where Zuse's own genesis marker/eligibility list
* already live (blk_meta_zone_read()/write(), devblock_from_top 0 and 1+
* respectively). This format deliberately does NOT go through
* blk_meta_zone_*, though: that accessor requires the device to already be
* of system metadata, and where Zuse's own genesis marker already lives
* (blk_meta_zone_read()/write(), devblock_from_top 0; 1-63 reserved,
* unused, since the eligibility list was removed 2026-10-14). This
* format deliberately does NOT go through blk_meta_zone_*, though: that accessor requires the device to already be
* blk_subsys_attach_device()'d and format-detected (first_disk_slot()) --
* exactly the state generic pre-attach discovery (repl.c's idle-loop
* USB-MSC scan) doesn't have yet, which is the entire reason this format
@@ -74,11 +74,8 @@
* the same top-of-device arithmetic independently via blkio_info(), so
* they work on a raw, not-yet-attached device exactly like
* homeblocks_sig_check() already does. ARTEMIS_SIG_DEVBLOCK_FROM_TOP is
* fixed well clear of Zuse's two tenants (0 and 1+, open-ended but
* realistically small -- 127 pubkeys per chained devblock) so the two
* subsystems' independent top-of-device math can never collide, without
* this format needing to know how far the eligibility chain has actually
* grown on any given boot.
* fixed well clear of Zuse's genesis marker (0) and the reserved,
* unused 1-63.
*
* Reserves offset/size pointers to the growable per-VM log-persistence
* region (FABRIC-3.md §XXVI follow-on's own log-record work), the same way
@@ -123,11 +120,10 @@ extern "C" {
* last devblock, 1 = second-to-last, ...), computed independently here via
* blkio_info() rather than through that accessor (which needs an already-
* attached, format-detected slot this code runs before). Fixed well past
* Zuse's genesis marker (devblock_from_top 0) and eligibility list
* (devblock_from_top 1, chained upward as needed) -- see
* zuse_eligibility_list.h -- so the two subsystems' independent top-of-
* device math can never collide regardless of how large the eligibility
* chain grows in practice. Well inside BLK_META_FENCE_INIT (128 devblocks,
* Zuse's genesis marker (devblock_from_top 0) and devblock_from_top
* 1-63 -- once Zuse's eligibility list, reserved and unused since that
* list was removed with word-ACL elevation (2026-10-14, FABRIC-3.md
* SXXXVII) -- so nothing here can collide with what stood there. Well inside BLK_META_FENCE_INIT (128 devblocks,
* block_subsystem.h) on any real Artemis disk. */
#define ARTEMIS_SIG_DEVBLOCK_FROM_TOP 64u
+4 -6
View File
@@ -31,7 +31,7 @@
* only artifact both platforms share is whatever got persisted to disk.
*
* Lives in the SAME top-of-device system-metadata fence artemis_sig_t and
* Zuse's genesis marker/eligibility list already use
* Zuse's genesis marker already uses
* (block_subsystem.h's blk_meta_zone_read()/write(), devblock_from_top
* addressing) -- reached the SAME way, deliberately NOT through
* artemis_sig.c's own independent blkio_info()-based arithmetic. That
@@ -52,11 +52,9 @@
* 64 -- artemis_sig_t itself (ARTEMIS_SIG_DEVBLOCK_FROM_TOP)
* 65 -- this ring's control header (LOG_REGION_DEVBLOCK_FROM_TOP_BASE)
* 66-96 -- this ring's slot devblocks, growable up to LOG_REGION_MAX_DEVBLOCKS
* Chosen clear of Zuse's genesis marker (devblock_from_top=0) and
* eligibility list (chains upward from 1, unbounded in code -- see
* zuse_eligibility_list.h's own CORRECTION comment, updated alongside this
* file, for the honest real-world headroom that leaves: devblocks 1-63,
* ~8000 possible eligible identities before ever reaching 64).
* Chosen clear of Zuse's genesis marker (devblock_from_top=0) and of
* devblocks 1-63, reserved and unused since Zuse's eligibility list was
* removed (2026-10-14, FABRIC-3.md SXXXVII).
*
* Ring granularity is one whole devblock-quarter (LOG_SLOT_SIZE, 1024
* bytes = exactly one blkio forth-block) per record -- avoids any
+4 -14
View File
@@ -734,19 +734,9 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
* that type's real cutover, not a fresh kernel-Hermes-space number. */
#define SK_HERMES_MSG_TYPE_CONSOLE_CMD 7
/* SK_HERMES_MSG_TYPE_ELEVATE_REQUEST (FABRIC-3.6.md task 3.10, Stage D)
* -- reuses messaging.4th's own now-historical ELEVATE-REQUEST value (8).
* At task 3.10, SEND-ELEVATE-REQUEST/ELEVATE-GRANT/CH-REQUEST all FIND
* live in Hera's own dictionary and SEND-ELEVATE-REQUEST was a real,
* complete, directly-callable entrypoint (H.5/H.8's own design: any VM
* wanting word-ACL elevation calls it with its own pubkey and the target
* word name). FABRIC-3.6.md Phase 4, Stage E deleted messaging.4th
* (SEND-ELEVATE-REQUEST's only definition) -- KH-ELEVATE-SEND below has
* had no FORTH caller since, and ELEVATE-GRANT (zuse-eligibility.4th,
* still loaded at boot) is unreachable. Found, not fixed -- Phase 8 PKI
* decision needed before Phase 5 close-out, see FABRIC-3.6.md's own
* Phase 4 entry. */
#define SK_HERMES_MSG_TYPE_ELEVATE_REQUEST 8
/* Type 8 (ELEVATE-REQUEST, FABRIC-3.6.md task 3.10) is retired: word-ACL
* elevation and Zuse's eligibility list were removed 2026-10-14 (FABRIC-3.7,
* closed as not needed; FABRIC-3.md SXXXVII). Not reused. */
/* The five negotiation types above (20-24) were chosen clear of
* messaging.4th's own live/reserved type space (PAUSE-EVENT=2,
@@ -754,7 +744,7 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
* BLK-ATTACH-EVENT=9, MSG-NACKED=253, MSG-DELIVERED=255) precisely
* because none of them had a real cutover yet -- kernel-Hermes stayed
* its own inert, parallel type space for those. SK_HERMES_MSG_TYPE_
* BLK_ATTACH, _CONSOLE_CMD, and _ELEVATE_REQUEST above are the
* BLK_ATTACH and _CONSOLE_CMD above (and the retired 8) are the
* deliberate exceptions: tasks 3.8/3.9/3.10 ARE those types' real
* cutovers, so they reuse the FORTH-side value rather than picking a
* fresh one. */
@@ -1,56 +0,0 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* zuse_eligibility.h - Read/add/membership-check over the on-disk
* elevation eligibility list (FABRIC-2.md §H.5/§H.12 Phase 6,
* zuse_eligibility_list.h's zuse_eligibility_devblock_t chain). Zuse
* checks zuse_eligibility_is_member() before honoring any
* ELEVATE-REQUEST (§H.7/§H.8) -- a gating layer on top of the
* message-based elevation trigger, not a replacement for it. Adding an
* entry (zuse_eligibility_add()) is meant to be reachable only from a
* Zuse-only FORTH word (§H.12 item 19, gated by zuse_session), not
* called directly from arbitrary session code.
*/
#ifndef STARKERNEL_ZUSE_ELIGIBILITY_H
#define STARKERNEL_ZUSE_ELIGIBILITY_H
#ifdef __STARKERNEL__
#include <stdint.h>
/**
* zuse_eligibility_is_member - Check whether pubkey appears anywhere in
* the eligibility list chain. Fail-closed: an empty/nonexistent list, a
* corrupt/foreign devblock encountered mid-chain, or any I/O error all
* read as "not eligible" (0), never "eligible" -- this gates elevation,
* so an unreadable list must never be treated as permissive.
*
* @param pubkey 32-byte Ed25519 public key to look up.
* @return 1 if found, 0 otherwise (including on any error).
*/
int zuse_eligibility_is_member(const uint8_t pubkey[32]);
/**
* zuse_eligibility_add - Add pubkey to the eligibility list, creating
* the list's head devblock if it doesn't exist yet and chaining a fresh
* devblock onto the tail if the current tail is full. Idempotent: adding
* an already-present pubkey is a no-op success, not a duplicate entry.
*
* @param pubkey 32-byte Ed25519 public key to add.
* @return 0 on success (added, or already present), -1 on
* failure (fence write failed, fence budget exhausted, or
* a corrupt devblock was encountered mid-chain).
*/
int zuse_eligibility_add(const uint8_t pubkey[32]);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_ZUSE_ELIGIBILITY_H */
@@ -1,99 +0,0 @@
/*
* zuse_eligibility_list.h -- on-disk record format for Zuse's word/block
* elevation eligibility list (FABRIC-2.md §H.5/§H.12 Phase 6): a simple
* growable list of owner_pubkey[32] entries, no extra per-entry metadata
* ("simple list, no extra metadata, unless we find a reason this won't
* work"). Zuse checks this list before honoring any ELEVATE-REQUEST
* (§H.7/§H.8) -- gating layer on top of the message-based elevation
* trigger, not a replacement for it.
*
* Lives in the same top-of-device system-metadata fence as
* zuse_genesis_marker_t (block_subsystem.h's blk_meta_zone_read()/
* write(), Phase 8/§C), one slot over: the genesis marker owns
* devblock_from_top=0, this list starts at devblock_from_top=1. Growable
* across multiple devblocks via a singly-linked chain (each devblock's
* `next_devblock_from_top` points at the next one, ZULIST_NO_NEXT means
* "this is the last devblock in the chain") -- entries are appended by
* filling the current tail devblock, then chaining a fresh one out of the
* already-reserved BLK_META_FENCE_INIT budget once it's full. No fence
* growth logic is needed yet.
*
* CORRECTION (FABRIC-3.md SXXVI follow-on, Step 4, 2026-09-13): the fence
* is no longer this list's alone to grow into. artemis_sig_t now owns
* devblock_from_top=64 (include/starkernel/artemis_sig.h), and the
* per-VM log-persistence ring now owns devblock_from_top 65-96
* (include/starkernel/log_region.h) -- both fixed, compile-time
* constants, chosen deliberately clear of this list's own growth
* direction (chaining upward from devblock_from_top=1). This list's real
* remaining headroom is devblocks 1-63 (not "126 more slots" as this
* comment used to say): 63 devblocks x ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK
* (127) = 8001 possible eligible identities before ever reaching
* devblock_from_top=64 -- vastly beyond any plausible real deployment of
* this project, but genuinely unenforced: there is no code-level ceiling
* stopping this chain from growing into devblock 64+ if that number were
* ever actually approached. Noted here, not silently assumed safe, after
* a fixed-offset collision was caught and fixed once already this session
* (Artemis's own disk vs. its live BAM, artemis_sig.h's own CORRECTION
* comment) -- the lesson being to state the real bound in writing rather
* than trust "it'll never get that big."
*
* Raw, unpacked 4 KiB devblock -- same convention as
* zuse_genesis_marker_t/homeblocks_sig_t: real CRC from day one, this
* gates a real security check, not a placeholder.
*/
#ifndef STARKERNEL_ZUSE_ELIGIBILITY_LIST_H
#define STARKERNEL_ZUSE_ELIGIBILITY_LIST_H
#include <stdint.h>
/* Packed via shifts, not a hand-computed hex literal -- see
* zuse_genesis_marker.h's own note on this project's standing
* numeric-constant convention. */
#define ZUSE_ELIGIBILITY_LIST_MAGIC \
((uint32_t)'Z' | ((uint32_t)'E' << 8) | ((uint32_t)'L' << 16) | ((uint32_t)'G' << 24))
#define ZUSE_ELIGIBILITY_LIST_VERSION 1u
/* devblock_from_top of this list's head devblock -- one past the genesis
* marker's devblock_from_top=0. */
#define ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK 1u
/* next_devblock_from_top value meaning "no further devblock -- this is
* the tail of the chain." 0xFFFFFFFF can never be a real
* devblock_from_top (BLK_META_FENCE_INIT is 128), so it is unambiguous. */
#define ZUSE_ELIGIBILITY_LIST_NO_NEXT 0xFFFFFFFFu
/* How many owner_pubkey[32] entries fit in one devblock alongside the
* header/crc/next-pointer overhead below. */
#define ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK 127u
typedef struct {
uint32_t magic; /* ZUSE_ELIGIBILITY_LIST_MAGIC; anything else on
* the head devblock means "list not created
* yet" (blank/foreign bytes), not a
* format-corruption error. A non-head devblock
* is only ever read by following a real
* next_devblock_from_top link, so its own
* magic is still checked the same way to catch
* a corrupt/foreign devblock mid-chain. */
uint32_t version; /* ZUSE_ELIGIBILITY_LIST_VERSION */
uint32_t count; /* Number of valid entries in this devblock's
* own entries[] (0..ZUSE_ELIGIBILITY_ENTRIES_
* PER_DEVBLOCK), not a running total across the
* whole chain. */
uint32_t next_devblock_from_top; /* Next devblock in the chain, or
* ZUSE_ELIGIBILITY_LIST_NO_NEXT if this is the
* tail. */
uint8_t entries[ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK][32]; /* Ed25519
* public keys eligible for elevation. Only the
* first `count` entries are meaningful. */
uint64_t crc; /* CRC-64/ISO (block_subsystem.h's
* compute_crc64()) over every byte of this
* struct up to (not including) this field. */
uint8_t _pad[4096 - (4 + 4 + 4 + 4 + (ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK * 32) + 8)];
} zuse_eligibility_devblock_t;
_Static_assert(sizeof(zuse_eligibility_devblock_t) == 4096,
"zuse_eligibility_devblock_t must be exactly one 4 KiB devblock");
#endif /* STARKERNEL_ZUSE_ELIGIBILITY_LIST_H */