Phase 8 v3: refuse a same-VM, cross-device raw block copy from within StarshipOS
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s

Two research passes confirmed the identity record itself (seed/pubkey/
cert) is already unreachable from any FORTH primitive -- only C-level
read_devblock/write_devblock touch it. But a device's ordinary user block
content CAN be copied between two attached devices today, using only
stock, unpinned words: <src> BLOCK <dst> BUFFER 1024 MOVE UPDATE
SAVE-BUFFERS, or the dedicated RELOCATE-BLOCK word (whose own doc comment
already admits "performs no policy validation of its own"). Checked
whether the existing per-block owner_fp/BLK-ACL-ALLOW@ metadata already
solves this -- it doesn't: owner_fp encodes who (a VM identity pubkey),
never where (physical device), and blk_get_buffer()/blk_update() never
consult acl_allow/acl_ttl at all -- those fields are completely inert.

Small, targeted fix, no rearchitecture:

- blk_subsys_relocate_block() (block_subsystem.c): same-device check.
  Its own documented purpose is wear-leveling (relocate on the SAME
  device) -- never stated as cross-device, and nothing enforced that
  until now.
- New public blk_lbn_device_handle() (block_subsystem.c/.h): the missing
  LBN-to-device direction (blk_get_device_range() already goes the other
  way). Opaque, stable, == comparable.
- MOVE (memory_words.c) and CMOVE/CMOVE> (string_words.c): refuse when
  both addresses are block-window addresses backed by two different
  devices -- the exact shape of the composed attack. A copy where either
  end is ordinary VM memory (the overwhelming common case: staging text
  from PAD, editing a block in place) is untouched.
- blk_vm_check_epoch()/blk_vm_slot_for_addr() exposed (block_words.h) so
  the two new call sites share the same window-slot invalidation contract
  rather than a second, divergent copy of it.

Verified live on all three architectures, not just boot-clean: same-
device MOVE/RELOCATE-BLOCK still succeed exactly as before; cross-device
MOVE/CMOVE/RELOCATE-BLOCK all refused. Zero UNKNOWN WORD, identical
dict_hash across all three (this change adds no FORTH-visible word, only
internal refusal conditions, as predicted).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-23 04:44:22 -04:00
co-authored by Claude Sonnet 5
parent b7d9ed5425
commit 26c1117ccd
12 changed files with 27605 additions and 1 deletions
+35
View File
@@ -158,4 +158,39 @@ void blk_vm_flush_all(VM * vm);
*/
void empty_all_buffers(VM * vm);
/**
* @brief Discards vm->blk_vm_lbn[]/cbuf[]/dirty[] if the device chain has
* changed since last validated (see this function's own doc comment in
* block_words.c for why). Exposed (Phase 8 v3, 2026-09-23) so callers
* outside this file that read vm->blk_vm_lbn[] directly (MOVE/CMOVE's
* cross-device block-copy check, memory_words.c/string_words.c) go through
* the same invalidation contract blk_vm_find() already uses internally,
* rather than a second, divergent copy of this logic.
* @param vm Pointer to the Forth virtual machine instance
*/
void blk_vm_check_epoch(VM *vm);
/**
* @brief Does `addr` fall inside the block I/O window? Pure address
* arithmetic against the window's fixed compile-time base/size -- no VM
* state needed. Exposed (Phase 8 v3, 2026-09-23) for MOVE/CMOVE/CMOVE>'s
* cross-device block-copy check.
* @param addr VM offset to test
* @return slot index (0..BLK_VM_SLOTS-1) if inside the window, -1 otherwise
*/
int blk_vm_slot_for_addr(vaddr_t addr);
/**
* @brief True if addr1/addr2 are both block-window addresses currently
* loaded from two different physical devices (Phase 8 v3, 2026-09-23) --
* the check MOVE/CMOVE/CMOVE> call before copying to refuse a same-VM,
* cross-device raw block copy. See this function's own doc comment in
* block_words.c for exactly what is and isn't flagged.
* @param vm VM performing the copy
* @param addr1 first VM offset (source or destination, order doesn't matter)
* @param addr2 second VM offset
* @return 1 if this copy should be refused, 0 otherwise
*/
int blk_vm_is_cross_device_copy(VM *vm, vaddr_t addr1, vaddr_t addr2);
#endif /* BLOCK_WORDS_H */