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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -49,7 +49,6 @@
|
||||
#include "starkernel/capsule_mint.h"
|
||||
#include "starkernel/user_identity_seed.h"
|
||||
#include "starkernel/vm_identity.h"
|
||||
#include "starkernel/zuse_eligibility.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
#include "starkernel/capsule_run.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
@@ -1345,70 +1344,6 @@ static void mama_word_mint_scratch_emit(VM *vm)
|
||||
vm_push(vm, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ZUSE-ELIGIBILITY-ADD ( c-addr -- ok? )
|
||||
* Add the 32-byte Ed25519 public key at c-addr to Zuse's elevation
|
||||
* eligibility list (FABRIC-2.md §H.5/§H.12 item 19). Plain, unconditional
|
||||
* primitive -- no authorization check here or anywhere else in this
|
||||
* codebase gates on vm->zuse_session. Zuse's authority is the *absence*
|
||||
* of any ACL restricting her, not a bit this or any other word checks;
|
||||
* restricting who may call this word, if ever wanted, is the same
|
||||
* standing word-level ACL mechanism (acl_allow/ACL-PIN in ACL.4th) any
|
||||
* other word would use, applied later if and when actually needed --
|
||||
* not invented here.
|
||||
*/
|
||||
static void mama_word_zuse_eligibility_add(VM *vm)
|
||||
{
|
||||
if (vm->dsp < 0) {
|
||||
console_println("ZUSE-ELIGIBILITY-ADD: stack underflow");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
cell_t caddr = vm_pop(vm);
|
||||
const uint8_t *pubkey = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!pubkey) {
|
||||
console_println("ZUSE-ELIGIBILITY-ADD: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (zuse_eligibility_add(pubkey) != 0) {
|
||||
console_println("ZUSE-ELIGIBILITY-ADD: FAILED -- fence write error");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
vm_push(vm, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ZUSE-ELIGIBLE? ( c-addr -- flag )
|
||||
* Membership check over Zuse's elevation eligibility list (FABRIC-2.md
|
||||
* §H.5/§H.12 item 21). Plain, unconditional wrapper over
|
||||
* zuse_eligibility_is_member() -- same "no bespoke gate" convention as
|
||||
* ZUSE-ELIGIBILITY-ADD above; is_member() itself is already fail-closed.
|
||||
*/
|
||||
static void mama_word_zuse_eligible_query(VM *vm)
|
||||
{
|
||||
if (vm->dsp < 0) {
|
||||
log_message(LOG_ERROR, "ZUSE-ELIGIBLE?: stack underflow");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
cell_t caddr = vm_pop(vm);
|
||||
const uint8_t *pubkey = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!pubkey) {
|
||||
log_message(LOG_ERROR, "ZUSE-ELIGIBLE?: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
vm_push(vm, zuse_eligibility_is_member(pubkey) ? -1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief NAME>XT ( c-addr u -- xt|0 )
|
||||
* Dynamic dictionary lookup for a name already sitting in a data buffer
|
||||
@@ -1444,41 +1379,6 @@ static void mama_word_name_to_xt(VM *vm)
|
||||
vm_push(vm, (cell_t)(uintptr_t)e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ELEVATE-PUBKEY-UNPACK ( pk0 pk1 pk2 pk3 buf-addr -- )
|
||||
* Reconstructs a 32-byte Ed25519 pubkey from 4 cells into the 32-byte
|
||||
* buffer at buf-addr, matching ZUSE-PUBKEY@'s own convention exactly:
|
||||
* chunk i (0..3) is the 8-byte little-endian encoding of pubkey bytes
|
||||
* [i*8 .. i*8+7] -- this is that packing's inverse, so a pubkey sent as
|
||||
* 4 cells (the convention SEND-ELEVATE-REQUEST used before it was
|
||||
* deleted with messaging.4th, FABRIC-3.6.md Phase 4 Stage E) round-trips
|
||||
* byte-exact regardless of any individual chunk's sign bit.
|
||||
*/
|
||||
static void mama_word_elevate_pubkey_unpack(VM *vm)
|
||||
{
|
||||
if (vm->dsp < 4) {
|
||||
log_message(LOG_ERROR, "ELEVATE-PUBKEY-UNPACK: stack underflow");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
cell_t buf_addr = vm_pop(vm);
|
||||
cell_t pk[4];
|
||||
for (int i = 3; i >= 0; i--) pk[i] = vm_pop(vm);
|
||||
|
||||
uint8_t *buf = vm_ptr(vm, (vaddr_t)buf_addr);
|
||||
if (!buf) {
|
||||
log_message(LOG_ERROR, "ELEVATE-PUBKEY-UNPACK: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint64_t chunk = (uint64_t)pk[i];
|
||||
for (int b = 0; b < 8; b++) {
|
||||
buf[i * 8 + b] = (uint8_t)(chunk >> (8 * b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RUNCAP-TEST ( caddr u -- ok? rc )
|
||||
* Diagnostic-only word (FABRIC-2.md §F.6/§F.18): calls
|
||||
@@ -2375,10 +2275,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
|
||||
register_word(vm, "NAME>XT", mama_word_name_to_xt);
|
||||
register_word(vm, "ELEVATE-PUBKEY-UNPACK", mama_word_elevate_pubkey_unpack);
|
||||
register_word(vm, "RUNCAP-TEST", mama_word_runcap_test);
|
||||
register_word(vm, "PAIR-TEST", mama_word_pair_test);
|
||||
register_word(vm, "MAMA-VM-ID", mama_word_mama_vm_id);
|
||||
@@ -2440,10 +2337,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
|
||||
register_word(vm, "NAME>XT", mama_word_name_to_xt);
|
||||
register_word(vm, "ELEVATE-PUBKEY-UNPACK", mama_word_elevate_pubkey_unpack);
|
||||
register_word(vm, "RUNCAP-TEST", mama_word_runcap_test);
|
||||
register_word(vm, "PAIR-TEST", mama_word_pair_test);
|
||||
register_word(vm, "MAMA-VM-ID", mama_word_mama_vm_id);
|
||||
|
||||
@@ -1,107 +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
|
||||
*/
|
||||
|
||||
#ifndef __STARKERNEL__
|
||||
#error "zuse_eligibility.c is kernel-only"
|
||||
#endif
|
||||
|
||||
#include "starkernel/zuse_eligibility.h"
|
||||
#include "starkernel/zuse_eligibility_list.h"
|
||||
#include "block_subsystem.h" /* compute_crc64(), blk_meta_zone_read/write */
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Read and validate one devblock of the chain. Mirrors
|
||||
* capsule_zuse_boot.c's genesis_marker_read() convention exactly: magic
|
||||
* mismatch, version mismatch, and CRC mismatch are all merged into a
|
||||
* single -1 ("treat as absent/corrupt, not present") -- the fence region
|
||||
* is never zeroed at format time (block_subsystem.c's blk_commit_format()
|
||||
* only zeroes BAM/reloc), so blank foreign bytes and genuine corruption
|
||||
* are indistinguishable and must be handled identically here. */
|
||||
static int list_devblock_read(uint32_t devblock_from_top, zuse_eligibility_devblock_t *out) {
|
||||
if (blk_meta_zone_read(devblock_from_top, (uint8_t *)out) != 0) return -1;
|
||||
if (out->magic != ZUSE_ELIGIBILITY_LIST_MAGIC) return -1;
|
||||
if (out->version != ZUSE_ELIGIBILITY_LIST_VERSION) return -1;
|
||||
if (out->count > ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) return -1;
|
||||
uint64_t want_crc = compute_crc64((const uint8_t *)out,
|
||||
offsetof(zuse_eligibility_devblock_t, crc));
|
||||
if (want_crc != out->crc) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int list_devblock_write(uint32_t devblock_from_top, zuse_eligibility_devblock_t *blk) {
|
||||
blk->crc = compute_crc64((const uint8_t *)blk, offsetof(zuse_eligibility_devblock_t, crc));
|
||||
return blk_meta_zone_write(devblock_from_top, (const uint8_t *)blk) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int zuse_eligibility_is_member(const uint8_t pubkey[32]) {
|
||||
uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK;
|
||||
zuse_eligibility_devblock_t blk;
|
||||
|
||||
for (;;) {
|
||||
if (list_devblock_read(devblock, &blk) != 0) return 0; /* fail-closed */
|
||||
for (uint32_t i = 0; i < blk.count; i++) {
|
||||
if (memcmp(blk.entries[i], pubkey, 32) == 0) return 1;
|
||||
}
|
||||
if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) return 0;
|
||||
devblock = blk.next_devblock_from_top;
|
||||
}
|
||||
}
|
||||
|
||||
int zuse_eligibility_add(const uint8_t pubkey[32]) {
|
||||
uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK;
|
||||
zuse_eligibility_devblock_t blk;
|
||||
|
||||
if (list_devblock_read(devblock, &blk) != 0) {
|
||||
/* List doesn't exist yet (blank fence, or this is genuinely the
|
||||
* first entry ever added) -- create the head devblock fresh. */
|
||||
memset(&blk, 0, sizeof(blk));
|
||||
blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC;
|
||||
blk.version = ZUSE_ELIGIBILITY_LIST_VERSION;
|
||||
blk.count = 1;
|
||||
blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT;
|
||||
memcpy(blk.entries[0], pubkey, 32);
|
||||
return list_devblock_write(devblock, &blk);
|
||||
}
|
||||
|
||||
/* Walk the existing chain: bail out early (idempotent success) if
|
||||
* already present, otherwise track the tail devblock to append to. */
|
||||
for (;;) {
|
||||
for (uint32_t i = 0; i < blk.count; i++) {
|
||||
if (memcmp(blk.entries[i], pubkey, 32) == 0) return 0; /* already eligible */
|
||||
}
|
||||
if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) break;
|
||||
devblock = blk.next_devblock_from_top;
|
||||
if (list_devblock_read(devblock, &blk) != 0) return -1; /* corrupt mid-chain */
|
||||
}
|
||||
|
||||
if (blk.count < ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) {
|
||||
memcpy(blk.entries[blk.count], pubkey, 32);
|
||||
blk.count++;
|
||||
return list_devblock_write(devblock, &blk);
|
||||
}
|
||||
|
||||
/* Tail devblock is full -- chain a fresh one drawn from the
|
||||
* already-reserved fence budget, one devblock_from_top past the
|
||||
* current tail (blk_meta_zone_write() itself refuses any offset at
|
||||
* or beyond the on-disk meta_fence_blocks reservation, so an
|
||||
* exhausted budget surfaces here as an ordinary write failure). */
|
||||
uint32_t new_devblock = devblock + 1;
|
||||
zuse_eligibility_devblock_t new_blk;
|
||||
memset(&new_blk, 0, sizeof(new_blk));
|
||||
new_blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC;
|
||||
new_blk.version = ZUSE_ELIGIBILITY_LIST_VERSION;
|
||||
new_blk.count = 1;
|
||||
new_blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT;
|
||||
memcpy(new_blk.entries[0], pubkey, 32);
|
||||
if (list_devblock_write(new_devblock, &new_blk) != 0) return -1;
|
||||
|
||||
blk.next_devblock_from_top = new_devblock;
|
||||
return list_devblock_write(devblock, &blk);
|
||||
}
|
||||
@@ -96,9 +96,8 @@ static int log_region_write_slot(uint32_t slot_index, const log_slot_t *slot) {
|
||||
* granularity, and a devblock holds LOG_SLOTS_PER_DEVBLOCK slots, so a
|
||||
* single-slot write must preserve its siblings. A read failure on a
|
||||
* never-yet-written devblock (blank fence content) is not fatal here --
|
||||
* proceed with a zeroed buffer, matching zuse_eligibility.c's own
|
||||
* "blank fence content is indistinguishable from absent, treat as
|
||||
* absent" discipline. */
|
||||
* proceed with a zeroed buffer: blank fence content is
|
||||
* indistinguishable from absent, and is treated as absent. */
|
||||
if (blk_meta_zone_read(dft, devblock_buf) != 0) {
|
||||
memset(devblock_buf, 0, sizeof(devblock_buf));
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
@@ -656,26 +656,13 @@ int sk_hermes_drain_checkpoint(VM *vm) {
|
||||
console_println(line);
|
||||
}
|
||||
|
||||
/* FABRIC-3.6.md task 3.10 evidence: ELEVATE-REQUEST also has no fixed
|
||||
* C-side handler word -- its payload is a FORTH command string
|
||||
* calling ELEVATE-GRANT (zuse-eligibility.4th), interpreted as-is
|
||||
* below, same shape as CONSOLE-CMD-EVENT above. Same mid-hold-instant
|
||||
* evidence, same reasoning; `self` here is always Hera (`vm`'s own
|
||||
* caller already resolved it that way in KH-ELEVATE-SEND, since
|
||||
* ELEVATE-GRANT only ever runs on Hera). */
|
||||
if (msg->type == SK_HERMES_MSG_TYPE_ELEVATE_REQUEST) {
|
||||
uint64_t held, pulled, returned, consumed;
|
||||
char line[160];
|
||||
int conserved = stadium_conserved(self);
|
||||
sk_hermes_ledger(&held, &pulled, &returned, &consumed);
|
||||
snprintf(line, sizeof(line),
|
||||
"Kernel-Hermes ELEVATE-REQUEST (real, Stage D): ledger held=%llu "
|
||||
"pulled=%llu returned=%llu consumed=%llu stadium_conserved(self)=%s",
|
||||
(unsigned long long)held, (unsigned long long)pulled,
|
||||
(unsigned long long)returned, (unsigned long long)consumed,
|
||||
conserved ? "true" : "FALSE");
|
||||
console_println(line);
|
||||
}
|
||||
/* Off the queue BEFORE it runs (2026-10-14): running it can reach a
|
||||
* colon-word checkpoint that drains this same VM again, and that
|
||||
* drain must find the next message, not this one -- popped only after,
|
||||
* a message whose payload calls a colon word ran twice (FABRIC-3.md
|
||||
* SXXXVII). The message itself, and so its payload bytes, is still
|
||||
* held until sk_hermes_release() below. */
|
||||
sk_hermes_pending_pop(self);
|
||||
|
||||
vm->mode = MODE_INTERPRET; /* a payload is never compiled into whatever
|
||||
* the enclosing context was mid-defining */
|
||||
@@ -691,7 +678,6 @@ int sk_hermes_drain_checkpoint(VM *vm) {
|
||||
vm->abort_requested = saved_abort;
|
||||
|
||||
sk_hermes_release(msg);
|
||||
sk_hermes_pending_pop(self);
|
||||
|
||||
return drain_error ? -1 : 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user