Phase 8 v3: refuse a same-VM, cross-device raw block copy from within StarshipOS
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:
co-authored by
Claude Sonnet 5
parent
b7d9ed5425
commit
26c1117ccd
@@ -805,6 +805,18 @@ int blk_subsys_relocate_block(uint32_t home_lbn, uint32_t target_lbn) {
|
||||
|
||||
if (!blk_is_valid(home_lbn) || !blk_is_valid(target_lbn)) return BLK_ERANGE;
|
||||
|
||||
/* Same-device only (Phase 8 v3, 2026-09-23): this function's own purpose
|
||||
* is relocating a block to a new LBN on the device it's being relocated
|
||||
* to (wear-leveling) -- never documented as cross-device, and nothing
|
||||
* enforced that until now. Without this check, RELOCATE-BLOCK is a
|
||||
* ready-made, unpinned, silent cross-device raw block copy primitive --
|
||||
* closing that is the whole point of this change. */
|
||||
if (lbn_to_slot(resolve_lbn(home_lbn)) != lbn_to_slot(resolve_lbn(target_lbn))) {
|
||||
log_message(LOG_WARN,
|
||||
"blk: refused cross-device relocate LBN %u -> %u", home_lbn, target_lbn);
|
||||
return BLK_EINVAL;
|
||||
}
|
||||
|
||||
/* Stage through a local buffer rather than copying directly from one
|
||||
* blk_get_buffer() result to another -- obtaining the target buffer
|
||||
* can trigger a cache eviction (cache_get_slot()'s FIFO shift) that
|
||||
@@ -1194,6 +1206,10 @@ int blk_get_device_range(struct blkio_dev *dev, uint32_t *out_start_lbn, uint32_
|
||||
return BLK_OK;
|
||||
}
|
||||
|
||||
const void *blk_lbn_device_handle(uint32_t lbn) {
|
||||
return (const void *) lbn_to_slot(resolve_lbn(lbn));
|
||||
}
|
||||
|
||||
int blk_get_device_free_blocks(struct blkio_dev *dev, uint64_t *out_free, uint64_t *out_total) {
|
||||
if (!dev || !out_free || !out_total) return BLK_EINVAL;
|
||||
blk_dev_slot_t *slot = slot_by_dev(dev);
|
||||
|
||||
Reference in New Issue
Block a user