117 lines
5.7 KiB
Markdown
117 lines
5.7 KiB
Markdown
# Capsule Loader — Missing Persistence Boundary Check
|
||
|
||
**Date:** 2026-07-09
|
||
**Branch:** `lithosananke`
|
||
**Status:** CLOSED — not a bug. Captain Bob's direct correction
|
||
(2026-07-10), after this finding drove a design doc
|
||
(`CAPSULE-LOADER-DEFINITIONS-ONLY-DESIGN-20260709.md`) and a real
|
||
implementation that removed the write entirely — both wrong, both
|
||
reverted (commits `43d81d49`/`548062ab`, three-arch Tripod acceptance
|
||
re-confirmed at `ee91280d`): *"all capsule does is LOAD a block with a
|
||
given Block xxxxx header to THAT BLOCK. that's all. They are not ordered
|
||
in the capsule necessarily."* The behavior documented below —
|
||
`write_ramdrive_block` writing every capsule's content to the literal LBN
|
||
named by its own `Block N` header, unconditionally, including onto
|
||
Artemis's real disk for `4000+`-numbered capsules — is the intended
|
||
mechanism, not a boundary violation. Neither candidate fix in "Not fixed"
|
||
below applies; no fix is needed. Kept as a record of a wrong turn, per
|
||
this repo's revision-history convention, not deleted.
|
||
**Author:** Captain Bob / Claude Code
|
||
|
||
Surfaced while scoping `HERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md`
|
||
(picking an LBN range for Hermes's new message-block storage) — not
|
||
something either doc set out to find, but relevant to both.
|
||
|
||
---
|
||
|
||
## The architecture, as confirmed by Captain Bob
|
||
|
||
Inside a VM, block-addressed storage has two internal surfaces:
|
||
|
||
- **RAM surface** — LBN 0–2047. Fast working memory, presented in block
|
||
shape.
|
||
- **Ramdrive** — LBN 2048–3071. A mounted block device, still internal
|
||
to the VM (volatile, RAM-backed via `krd_buf`) — not real disk.
|
||
|
||
Both are internal to the VM. Capsule persistence mechanics — the part of
|
||
capsule loading that writes a capsule's parsed blocks somewhere so a
|
||
later `LOAD N` can find them — are meant to reach only into these two
|
||
surfaces, never past LBN 3071 into a real external device.
|
||
|
||
## The defect
|
||
|
||
`write_ramdrive_block()` (`src/starkernel/capsule/capsule_loader.c:146`)
|
||
has no boundary check. It calls `blk_get_buffer(block_num, 1)`
|
||
(`src/block_subsystem.c:576`) with the literal `block_num` taken from a
|
||
capsule's own `Block N` source header — unconditionally, regardless of
|
||
value.
|
||
|
||
`blk_get_buffer()` resolves any LBN against the full chain of registered
|
||
block devices, in registration order (`src/block_subsystem.c:491-546`):
|
||
LBN 0–2047 → RAM surface (`g.ram_user = BLK_RAM_BLOCKS(2080) -
|
||
BLK_FORTH_SYS_RESERVED(32) = 2048`), LBN 2048–3071 → the ramdrive raw
|
||
device (`KRD_MAX_BLOCKS = 1024`, registered in
|
||
`capsule_blk_init()`), LBN 3072+ → whatever device is attached next. In
|
||
the current boot sequence that next device is Artemis's virtio-blk disk,
|
||
attached in `kernel_main.c:520-530` via `virtio_blk_find_artemis()` +
|
||
`blk_subsys_attach_device()`, whose own `start_lbn` is `3072`
|
||
(`artemis/init.4th`'s own `3072 CONSTANT ART-HDR-LBN`, matching the
|
||
`total_user_lbn` accumulated by the two prior device registrations).
|
||
|
||
So a capsule whose `Block N` headers are `≥ 3072` has its content written
|
||
by `write_ramdrive_block()` directly onto whatever real device occupies
|
||
that LBN — currently Artemis's persistent disk — as an unbounded side
|
||
effect of capsule loading. This happens on every VM birth, not just the
|
||
interactive `EXEC` word: `capsule_birth_set_hooks()`
|
||
(`src/starkernel/capsule/capsule_vm_hooks.c:121-124`) registers
|
||
`capsule_exec_hook` (which wraps `capsule_exec_payload`) as the
|
||
`vm_exec_fn` used by both `capsule_birth_mama()` and
|
||
`capsule_birth_baby()` (`src/starkernel/capsule/capsule_birth.c:374,
|
||
449`).
|
||
|
||
**One corroborating detail:** `capsule_loader.c:50-51` defines
|
||
`CAPSULE_RAM_OFFSET 2048u` with the comment `dest = source -
|
||
CAPSULE_RAM_OFFSET`, but `write_ramdrive_block()` never references this
|
||
macro — `block_num` is passed to `blk_get_buffer()` unmodified. The macro
|
||
is unused dead code; whatever offsetting scheme its comment describes was
|
||
never implemented.
|
||
|
||
## Scope — which capsules are affected
|
||
|
||
From `capsules/BLOCK_MAP.md` (auto-generated), every capsule with `Block
|
||
N` headers `≥ 4000` is exposed: `ACL.4th` (4000–4007, 4015), `zuse.4th`
|
||
(4016–4018), `lib.4th` (4050), `common:msg.4th` (4055),
|
||
`doe-campaign.4th` (4060–4065), `hermes:init.4th` (4100–4153),
|
||
**`artemis:init.4th` itself** (4110–4141, 4851–4852), and the
|
||
`init-1.4th` through `init-9.4th` / L8-variant capsules (4406+, 4506+,
|
||
4606+, 4706+, 4806–4842). Capsules whose headers stay within 2048–3071
|
||
(`init.4th`, `init-l8-omni.4th`, `init-6.4th`, `doe.4th`, `init-4.4th`,
|
||
`init-7.4th`, `init-8.4th`, `init-0.4th`) are unaffected — their writes
|
||
land in the volatile ramdrive regardless.
|
||
|
||
## Mitigating factor, also confirmed
|
||
|
||
`blk_get_buffer()` returns `NULL` for an LBN with no device registered
|
||
there; `write_ramdrive_block()` no-ops on a `NULL` buffer
|
||
(`capsule_loader.c:150-151`). So on any boot where Artemis's virtio-blk
|
||
disk isn't attached (`kernel_main.c:528`, "Artemis: no virtio-blk disk
|
||
(continuing without)"), every `4000+`-numbered capsule's persistence
|
||
write silently fails rather than landing anywhere — harmless in that
|
||
case, but also meaning `LOAD N` on those block numbers would return
|
||
whatever stale/zeroed content already occupies that LBN, not the
|
||
capsule's own text.
|
||
|
||
## Not fixed — because there is nothing to fix
|
||
|
||
Two candidate fix shapes were raised in conversation. Both are now moot,
|
||
confirmed directly:
|
||
|
||
1. A hard boundary check in `write_ramdrive_block()` — rejected. Nothing
|
||
is meant to reject or no-op a write based on `block_num`.
|
||
2. Renumber the `4000+` capsules' `Block N` headers back under 3072 —
|
||
also unnecessary; a capsule's header number naming a real block
|
||
anywhere in the address space, including Artemis's disk, is correct.
|
||
|
||
`write_ramdrive_block()` and its call site in `capsule_exec_payload`
|
||
remain exactly as they were before this finding was ever filed.
|