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:
co-authored by
Claude Opus 5.5
parent
029a8ed9cf
commit
5e26fad30d
+51
-94
@@ -260,6 +260,15 @@ static void sk_word_blk_attach_ack(VM *vm) {
|
||||
cell_t dev_addr = vm_pop(vm);
|
||||
blkio_dev_t *dev = (blkio_dev_t *)(uintptr_t)dev_addr;
|
||||
|
||||
/* Hera's own, run from Artemis's reply (2026-10-14): every VM has this
|
||||
* word (word_registry.c), and it births identities -- from any other
|
||||
* VM it is refused (FABRIC-3.md SXXXVII). */
|
||||
if (vm != (VM *)sk_get_mama_vm()) {
|
||||
console_println("BLK-ATTACH-ACK: refused -- Hera's own, from Artemis's reply");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_usb_blk_dev_slots || !g_blk_attach_pending) return;
|
||||
|
||||
uint32_t found_slot = 0;
|
||||
@@ -359,11 +368,9 @@ static void sk_word_blk_attach_ack(VM *vm) {
|
||||
* message type that actually flowed through a messaging layer.
|
||||
*
|
||||
* sk_hermes_drain_checkpoint() (task 3.4) calls vm_interpret() directly
|
||||
* on a message's payload_addr, so it must be NUL-terminated -- Artemis's
|
||||
* own ATTACH-ACK-BUF (artemis:init.4th) is a raw CMOVE'd byte buffer
|
||||
* with no such guarantee, so this copies into its own NUL-terminated
|
||||
* buffer rather than passing ATTACH-ACK-BUF's address through
|
||||
* unchanged.
|
||||
* on a message's payload_addr, so it must be NUL-terminated; since
|
||||
* 2026-10-14 the kernel writes the whole reply text into this buffer
|
||||
* itself (KH-BLK-ATTACH-SEND, below), from two numbers.
|
||||
*
|
||||
* CORRECTED 2026-09-22 (real defect, this task's own findings log):
|
||||
* this comment used to claim "static, not stack-local" was load-bearing
|
||||
@@ -383,47 +390,47 @@ static void sk_word_blk_attach_ack(VM *vm) {
|
||||
#define SK_KH_BLK_ATTACH_BUF_SIZE 80
|
||||
static char g_kh_blk_attach_buf[SK_KH_BLK_ATTACH_BUF_SIZE];
|
||||
|
||||
/* KH-BLK-ATTACH-SEND ( paddr plen -- ok? ): copies the caller's payload
|
||||
* (Artemis's own ATTACH-ACK-BUF content) into the NUL-terminated buffer
|
||||
* above and sends it to Hera via kernel-Hermes. `from`/`to` are derived
|
||||
* from the calling VM and sk_get_mama_vm() -- the caller never has to
|
||||
* name a VMUuid, matching how HERA-BLK-ATTACH-REQ never had to before
|
||||
* either (FORTH's own IDX 0/1/2 convention did that translation for
|
||||
* it). Refusal (reservoir/arena/destination-queue exhaustion) is logged
|
||||
* rather than silently dropped -- FABRIC-3.5.md SXXXV.0 names silent
|
||||
* failure as this project's single most common defect shape, and this
|
||||
* is exactly the class of message (identity birth gates on it) where a
|
||||
* silent drop would be expensive to ever notice. */
|
||||
/* KH-BLK-ATTACH-SEND ( dev-addr ok? -- sent? ): Artemis's storage-attach
|
||||
* reply to Hera. The kernel builds the message text itself --
|
||||
* "<dev-addr> <ok?> BLK-ATTACH-ACK" -- from the two numbers, and only for
|
||||
* Artemis. Until 2026-10-14 it took ( paddr plen ), any text at all, from
|
||||
* any VM, and Hera ran that text in her own dictionary, Zuse's: a
|
||||
* WIREBIND identity could have any FORTH run as Zuse (shown live,
|
||||
* FABRIC-3.md SXXXVII). `from`/`to` are derived from the calling VM and
|
||||
* sk_get_mama_vm() -- the caller never names a VMUuid. Refusal
|
||||
* (reservoir/arena/destination-queue exhaustion) is logged rather than
|
||||
* silently dropped -- FABRIC-3.5.md SXXXV.0 names silent failure as this
|
||||
* project's single most common defect shape, and identity birth gates on
|
||||
* this message. */
|
||||
static void sk_word_kh_blk_attach_send(VM *vm) {
|
||||
VMRegistryEntry artemis;
|
||||
VM *mama_vm = (VM *)sk_get_mama_vm();
|
||||
cell_t ok_flag, dev_addr;
|
||||
int n, rc;
|
||||
|
||||
if (vm->dsp < 1) {
|
||||
log_message(LOG_ERROR, "KH-BLK-ATTACH-SEND: stack underflow");
|
||||
console_println("KH-BLK-ATTACH-SEND: expects dev-addr ok? -- nothing on the stack");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
cell_t plen = vm_pop(vm);
|
||||
cell_t paddr = vm_pop(vm);
|
||||
/* paddr is a VM-relative offset (vaddr_t), not a host pointer --
|
||||
* CLAUDE.md's own "Important Conventions": "Stack values are VM
|
||||
* offsets (vaddr_t), not C pointers -- use VM_ADDR()/CELL()".
|
||||
* mama_forth_words.c's own VM-EXEC/VM-CALL words all translate a
|
||||
* popped paddr the same way (vm_ptr(vm, (vaddr_t)caddr)) before
|
||||
* touching it as a C pointer -- found live, not assumed: the first
|
||||
* cut here raw-cast paddr directly and silently read all-zero
|
||||
* memory (some unmapped/zeroed low address), producing an empty
|
||||
* NUL-terminated payload that vm_interpret() executed as a no-op --
|
||||
* no crash, no error, just a message that arrived and did nothing.
|
||||
* Diagnosed via a temporary probe printing the copied buffer content
|
||||
* at send time (per feedback_revert_probes_after_capture); reverted
|
||||
* once this fix confirmed correct. */
|
||||
const char *src = (const char *)vm_ptr(vm, (vaddr_t)paddr);
|
||||
size_t n = (size_t)plen;
|
||||
VM *mama_vm = (VM *)sk_get_mama_vm();
|
||||
size_t i;
|
||||
int rc;
|
||||
ok_flag = vm_pop(vm);
|
||||
dev_addr = vm_pop(vm);
|
||||
|
||||
if (n >= SK_KH_BLK_ATTACH_BUF_SIZE) n = SK_KH_BLK_ATTACH_BUF_SIZE - 1;
|
||||
for (i = 0; i < n; i++) g_kh_blk_attach_buf[i] = src[i];
|
||||
g_kh_blk_attach_buf[n] = '\0';
|
||||
if (capsule_vm_find_by_name_nocase("Artemis", &artemis) != 0 ||
|
||||
artemis.vm_ptr != (void *)vm) {
|
||||
console_println("KH-BLK-ATTACH-SEND: refused -- only Artemis replies to a storage attach");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
n = snprintf(g_kh_blk_attach_buf, sizeof(g_kh_blk_attach_buf),
|
||||
"%llu %u BLK-ATTACH-ACK",
|
||||
(unsigned long long)(uint64_t)dev_addr, ok_flag ? 1u : 0u);
|
||||
if (n <= 0 || (size_t)n >= sizeof(g_kh_blk_attach_buf)) {
|
||||
console_println("KH-BLK-ATTACH-SEND: FAILED -- reply does not fit");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sk_hermes_send_one(vm->stadium_vm_id, mama_vm->stadium_vm_id,
|
||||
SK_HERMES_MSG_TYPE_BLK_ATTACH, 0,
|
||||
@@ -434,64 +441,14 @@ static void sk_word_kh_blk_attach_send(VM *vm) {
|
||||
vm_push(vm, rc == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/* FABRIC-3.6.md task 3.10 (Stage D): sized to match messaging.4th's own
|
||||
* ELEVATE-REQ-BUF (256 bytes) -- the FORTH-side sender never builds a
|
||||
* longer payload than that buffer allows, so this is a safe match, not
|
||||
* an arbitrary choice. static, not stack-local: safe now that
|
||||
* sk_hermes_send_one() copies into the message's own storage (the
|
||||
* payload-aliasing fix landed before this task started) -- no lifetime
|
||||
* caveat to carry forward this time, unlike g_kh_blk_attach_buf/
|
||||
* g_kh_console_cmd_buf's own now-corrected history. */
|
||||
#define SK_KH_ELEVATE_BUF_SIZE 256
|
||||
static char g_kh_elevate_buf[SK_KH_ELEVATE_BUF_SIZE];
|
||||
|
||||
/* KH-ELEVATE-SEND ( paddr plen -- ok? ): built for messaging.4th's own
|
||||
* SEND-ELEVATE-REQUEST (a FORTH command string calling ELEVATE-GRANT,
|
||||
* zuse-eligibility.4th block 4022), which handed its payload here
|
||||
* instead of CH-REQUEST's old COMMON-CH/MSG-SEND path. `from`/`to` are
|
||||
* derived the same way KH-BLK-ATTACH-SEND's own already does (the
|
||||
* calling VM and sk_get_mama_vm() -- ELEVATE-GRANT always runs on Hera,
|
||||
* per zuse-eligibility.4th's own header comment) -- the caller never
|
||||
* supplies either, which is a real correctness improvement over the
|
||||
* FORTH path it replaced: CH-REQUEST's own "initiator-only gate...
|
||||
* refuses if from doesn't match MY-CH-ID" existed only because a caller
|
||||
* COULD claim to be a different VM by passing the wrong stack value;
|
||||
* deriving `from` from the C-level calling VM's own identity makes that
|
||||
* spoof structurally impossible rather than merely gated. FABRIC-3.6.md
|
||||
* Phase 4, Stage E deleted messaging.4th -- SEND-ELEVATE-REQUEST no
|
||||
* longer exists anywhere, so this word currently has no FORTH caller.
|
||||
* Found, not fixed; see FABRIC-3.6.md's own Phase 4 entry. */
|
||||
static void sk_word_kh_elevate_send(VM *vm) {
|
||||
if (vm->dsp < 1) {
|
||||
log_message(LOG_ERROR, "KH-ELEVATE-SEND: stack underflow");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
cell_t plen = vm_pop(vm);
|
||||
cell_t paddr = vm_pop(vm);
|
||||
const char *src = (const char *)vm_ptr(vm, (vaddr_t)paddr);
|
||||
size_t n = (size_t)plen;
|
||||
VM *mama_vm = (VM *)sk_get_mama_vm();
|
||||
size_t i;
|
||||
int rc;
|
||||
|
||||
if (n >= SK_KH_ELEVATE_BUF_SIZE) n = SK_KH_ELEVATE_BUF_SIZE - 1;
|
||||
for (i = 0; i < n; i++) g_kh_elevate_buf[i] = src[i];
|
||||
g_kh_elevate_buf[n] = '\0';
|
||||
|
||||
rc = sk_hermes_send_one(vm->stadium_vm_id, mama_vm->stadium_vm_id,
|
||||
SK_HERMES_MSG_TYPE_ELEVATE_REQUEST, 0,
|
||||
g_kh_elevate_buf, (uint32_t)(n + 1));
|
||||
if (rc != 0) {
|
||||
log_message(LOG_ERROR, "KH-ELEVATE-SEND: kernel-Hermes refused the send");
|
||||
}
|
||||
vm_push(vm, rc == 0 ? 1 : 0);
|
||||
}
|
||||
/* KH-ELEVATE-SEND was here until 2026-10-14: it sent any text to Hera as
|
||||
* an ELEVATE-REQUEST, and Hera ran it as Zuse. Removed with word-ACL
|
||||
* elevation itself (FABRIC-3.7, closed as not needed; FABRIC-3.md
|
||||
* SXXXVII). */
|
||||
|
||||
void sk_repl_register_words(VM *vm) {
|
||||
register_word(vm, "BLK-ATTACH-ACK", sk_word_blk_attach_ack);
|
||||
register_word(vm, "KH-BLK-ATTACH-SEND", sk_word_kh_blk_attach_send);
|
||||
register_word(vm, "KH-ELEVATE-SEND", sk_word_kh_elevate_send);
|
||||
}
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
Reference in New Issue
Block a user