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
+17 -1
View File
@@ -496,10 +496,26 @@ int sk_hermes_send_one(VMUuid from, VMUuid to, uint32_t type, uint32_t channel_i
if (sk_hermes_alloc(from, &msg) != 0) return -1;
/* Real defect, found 2026-09-22 (FABRIC-3.6.md task 3.8's own
* findings log): this used to store the caller's own payload_addr
* as-is, a pointer this function does not own. Two sends before
* either drains meant both messages pointed at the same
* caller-owned buffer -- whichever send wrote last silently won.
* Copy into this message's own payload_buf instead (kernel_hermes.h,
* sized to SK_HERMES_CHUNK_MAX_PAYLOAD, the bound already enforced
* above) and point payload_addr at that -- every message now owns
* its payload bytes, independent of whatever the caller does with
* its own buffer afterward. payload_len == 0 is a real, valid case
* (an empty payload) -- memcpy of 0 bytes is well-defined even with
* a NULL payload_addr -- guarded anyway (C's memcpy() is technically
* undefined for a NULL argument even at length 0), matching this
* project's own STRICT_PTR discipline elsewhere. */
if (payload_len > 0) memcpy(msg->payload_buf, payload_addr, payload_len);
msg->payload_addr = msg->payload_buf;
msg->type = type;
msg->from = from;
msg->to = to;
msg->payload_addr = payload_addr;
msg->payload_len = payload_len;
msg->channel = channel_id;