Fix SkHermesMessage payload-aliasing defect (task 3.8 findings log)

sk_hermes_send_one() -- the single funnel every sender, including
sk_hermes_publish(), already goes through -- used to store the
caller's own payload_addr pointer as-is. Two sends before either
drains meant both messages pointed at the same caller-owned buffer,
whichever send wrote last silently winning: real, confirmed live (a
second identity thumbdrive attached at boot alongside Zuse's own left
its WIREBIND pairing silently never happening).

SkHermesMessage gains an inline payload_buf[SK_HERMES_CHUNK_MAX_
PAYLOAD] field; sk_hermes_send_one() now memcpy()s the caller's
payload into it and points payload_addr at that copy instead. No
sender or reader call site needed to change -- every existing reader
already only ever reads through payload_addr, which still points at
valid bytes of the same length, now message-owned. g_kh_blk_attach_buf/
g_kh_console_cmd_buf (repl.c, tasks 3.8/3.9) no longer need to survive
past their own send call; their doc comments, which had claimed the
old aliasing shape was benign, are corrected.

Verified the original bug is actually gone: reproduced the exact
original scenario (a real, sequentially-minted rajames identity
attached at boot alongside Zuse's own, two simultaneous BLK-ATTACH-
EVENT sends in one idle-loop pass) -- WIREBIND pairing, USE, and the
Stage D relay all now work where WIREBIND previously silently failed.
Standard three-ISA acceptance also clean: zero UNKNOWN WORD, dict_hash
identical across all three and matching every prior acceptance run in
this document.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-22 14:16:29 -04:00
co-authored by Claude Sonnet 5
parent 21734b1a52
commit 5c07745c18
14 changed files with 46639 additions and 35 deletions
+42 -25
View File
@@ -362,12 +362,23 @@ static void sk_word_blk_attach_ack(VM *vm) {
* 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. Static, not stack-local: sk_hermes_send_one() stores
* payload_addr out-of-line and the caller must keep it alive until
* drained (kernel_hermes.h's own documented contract) -- same single-
* buffer-reuse shape ATTACH-ACK-BUF itself already had (a second attach
* before the first drains overwrites it), not a new hazard this
* introduces. */
* unchanged.
*
* CORRECTED 2026-09-22 (real defect, this task's own findings log):
* this comment used to claim "static, not stack-local" was load-bearing
* because sk_hermes_send_one() stored payload_addr out-of-line, the
* caller having to keep it alive until drained -- and that a second
* attach before the first drains overwriting this buffer was "the same
* shape ATTACH-ACK-BUF itself already had, not a new hazard." That
* claim was wrong: it WAS a new, real payload-aliasing defect (two
* sends before either drains both pointing at this same buffer,
* whichever wrote last silently winning), confirmed live and fixed at
* the source (kernel_hermes.c's sk_hermes_send_one() now copies into
* the message's own storage immediately, kernel_hermes.h's own doc
* comment on SkHermesMessage has the full account). This buffer no
* longer needs to survive past the KH-BLK-ATTACH-SEND call that sends
* it -- staying `static` here is now just a convenience (avoids an
* 80-byte stack frame), not a correctness requirement. */
#define SK_KH_BLK_ATTACH_BUF_SIZE 80
static char g_kh_blk_attach_buf[SK_KH_BLK_ATTACH_BUF_SIZE];
@@ -1387,25 +1398,31 @@ static int sk_repl_line_calls_use(const char *input)
return (len == 3) || (input[len - 4] == ' ' || input[len - 4] == '\t');
}
/* FABRIC-3.6.md task 3.9 -- static, not stack-local, for the same reason
* g_kh_blk_attach_buf (task 3.8) is: sk_hermes_send_one() stores
* payload_addr out-of-line and the caller must keep it alive until
* drained. Sized to the largest single payload kernel-Hermes will ever
* accept (SK_HERMES_CHUNK_MAX_PAYLOAD, task 3.5's one-block bound) --
* console lines up to INPUT_BUFFER_SIZE-1 (1024) content bytes are
* silently capped to SK_HERMES_CHUNK_MAX_PAYLOAD-1 (1023) to leave room
* for the NUL terminator sk_hermes_drain_checkpoint() requires; chunking
* a console line across multiple messages is out of this task's scope
* (task 3.5 built the primitives, not a sender for this use) and the cap
* is not expected to be reached by ordinary interactive typing. Only one
* console-proxy session exists today (console.c's own singleton, per
* memory project_multiseat_console_idea) so the single shared buffer
* carries the same payload-aliasing defect g_kh_blk_attach_buf has
* (FABRIC-3.6.md task 3.8's write-up wrongly called this shape benign;
* corrected in the findings log, entry "found while starting task 3.9",
* once task 3.8's own defect was found). Fix deferred: Captain Bob
* ruled 2026-09-22 to land task 3.9's send-side cutover first and fix
* the aliasing separately. */
/* FABRIC-3.6.md task 3.9. Sized to the largest single payload
* kernel-Hermes will ever accept (SK_HERMES_CHUNK_MAX_PAYLOAD, task
* 3.5's one-block bound) -- console lines up to INPUT_BUFFER_SIZE-1
* (1024) content bytes are silently capped to SK_HERMES_CHUNK_MAX_
* PAYLOAD-1 (1023) to leave room for the NUL terminator
* sk_hermes_drain_checkpoint() requires; chunking a console line across
* multiple messages is out of this task's scope (task 3.5 built the
* primitives, not a sender for this use) and the cap is not expected to
* be reached by ordinary interactive typing.
*
* CORRECTED 2026-09-22 (real defect, found while starting this task,
* fixed at the source): this comment originally justified `static, not
* stack-local` on the claim that sk_hermes_send_one() stored
* payload_addr out-of-line and the caller had to keep it alive until
* drained -- which was true, and which g_kh_blk_attach_buf (task 3.8)
* turned out to make into a genuine payload-aliasing defect (two sends
* before either drains, whichever wrote last silently winning). Fixed
* at the source (kernel_hermes.c's sk_hermes_send_one() now copies into
* the message's own storage immediately, kernel_hermes.h's own doc
* comment on SkHermesMessage has the full account) -- this buffer no
* longer needs to survive past the send call, for this or any future
* console-proxy session (console.c's own singleton today, per memory
* project_multiseat_console_idea). Staying `static` is now just a
* convenience (avoids a 1024-byte stack frame), not a correctness
* requirement. */
static char g_kh_console_cmd_buf[SK_HERMES_CHUNK_MAX_PAYLOAD];
static void sk_repl_dispatch_line(VM *vm, const char *input)