Bug-fix sweep: repl reentrancy, virtio/blocksys bounds, identity CRCs, LOG_LINE_MAX

Code review fixes, all compile clean (hosted gcc + aarch64/riscv64 kernel flags):

- repl.c (H1): reentrancy guards on the MSG-TICK idle pump. sk_repl_idle()
  now defers when Hera is mid-interpret (g_mama_interpreting) or when its
  own vm_interpret is on the stack (g_idle_pump_active), so a blocking
  KEY/EXPECT/QUERY inside a dispatched line can no longer re-enter the
  interpreter and clobber the in-flight input buffer.
- virtio_rng.c: clamp device-returned used_len to VRNG_BUF_SIZE before the
  caller's data_buf copy, closing a device-controlled OOB read.
- block_subsystem.c: first-write path now keys off created_time==0 instead
  of dead magic==0 so fresh blocks get a real created_time stamp; first_free/
  last_allocated fixed to absolute Forth LBNs (set in blk_compute_fresh_geometry
  from slot->start_lbn, no longer the wrong physical-BAM-index values from
  compute_totals_from_B); physical-bounds guard on blk_meta_zone_read/write
  prevents unsigned underflow on a corrupt fence >= device size.
- capsule_zuse_boot.c / capsule_wirebind.c: identity seed validated magic ->
  version -> CRC-64 (compute_crc64 over offsetof(crc)) before trusting it,
  so a corrupt/format-mismatched record is refused, never loaded.
- log.h / starkernel/log.h: unused LOG_LINE_MAX 256 renamed LOG_MSG_LINE_MAX
  to lift the include-order collision with vm.h's LOG_LINE_MAX 64; stale
  include-order comments dropped (kernel_main.c, shim.c, capsule_birth.c).
- FABRIC-3.md: three stale-doc carry-forward items closed [x] with cbe7b49
  notes.

Real KEY/?TERMINAL/QUERY/EXPECT bodies (console WIP):
- repl.h/repl.c: sk_console_getkey()/sk_console_key_available()/
  sk_console_readline() public bodies; non-destructive peek buffers the
  found byte so a following KEY returns it.
- shim.c: getchar()/fgetc()/fgets()/sf_terminal_ready() routed through the
  real console paths instead of stubs; sf_terminal_ready() in platform_io.h
  with sf_terminal_ready() implemented for the hosted build (linux/io.c,
  POSIX select on fd 0) wired into Makefile.
- io_words.c: ?TERMINAL now returns actual terminal-readiness, not constant 0.

Artifacts: minted disk/artemis.img + rebuilt lfs kernel; BLOCK_MAP.md,
doe csv + qemu log regenerated.
This commit is contained in:
Robert Allan James
2026-08-28 23:28:10 -04:00
parent a54e84b2d6
commit 5689c397fc
21 changed files with 9742 additions and 48 deletions
+32 -5
View File
@@ -367,10 +367,13 @@ static void compute_totals_from_B(blk_volume_meta_t *m) {
uint64_t payload4k = (m->total_devblocks > (1+B+R+F)) ? (m->total_devblocks - 1 - B - R - F) : 0;
uint64_t storable = 3ULL * payload4k;
m->total_blocks = (m->tracked_blocks < storable) ? m->tracked_blocks : storable;
uint64_t reserved = (uint64_t) BLK_DISK_SYS_RESERVED;
if (reserved > m->total_blocks) reserved = m->total_blocks;
m->first_free = BLK_DISK_SYS_RESERVED + reserved;
m->last_allocated = BLK_DISK_SYS_RESERVED + reserved - 1;
/* NOTE: first_free/last_allocated are intentionally NOT set here. They
* are absolute Forth LBN hints (readers in blk_allocate()/blk_mark_free()
* subtract slot->start_lbn), which this pure-geometry function cannot
* know -- it has no slot/LBN-base context. Setting them to a physical
* BAM index here made the first-free hint point at the wrong block; the
* slot's owner establishes them from slot->start_lbn instead (see
* blk_compute_fresh_geometry()). */
}
/* ===== physical BAM I/O (sync to/from slot->bam[]) ===== */
@@ -530,6 +533,14 @@ static void blk_compute_fresh_geometry(blk_dev_slot_t *slot) {
slot->vol_meta.meta_fence_blocks = BLK_META_FENCE_INIT;
compute_totals_from_B(&slot->vol_meta);
/* Allocation hints are absolute Forth LBNs (see the readers' note at
* compute_totals_from_B()). This slot's first user-visible block is at
* slot->start_lbn, so that is the true "next free" hint on a fresh
* volume -- anything else (e.g. a physical BAM index) made the hint
* point into the wrong block and left blk_allocate() guessing. */
slot->vol_meta.first_free = (uint64_t) slot->start_lbn;
slot->vol_meta.last_allocated = (uint64_t) slot->start_lbn - 1u;
if (sf_has_rtc()) slot->vol_meta.created_time = sf_realtime_ns();
else slot->vol_meta.created_time = sf_monotonic_ns();
@@ -941,7 +952,14 @@ int blk_update(uint32_t block_num) {
uint8_t *blkdata = c->data + pack * BLK_FORTH_SIZE;
c->meta[pack].checksum = compute_crc64(blkdata, BLK_FORTH_SIZE);
uint64_t now = blk_get_timestamp();
if (c->meta[pack].magic == 0) {
/* cache_load_devblock() already force-stamps magic on every load, so
* testing magic==0 here is dead code that silently dropped the
* first-write created_time stamp. A block that has never been written
* before has created_time==0 (the loaded slot was memset); an existing
* block carries its stamped-on-first-write non-zero created_time. Use
* that as the first-write signal so a fresh block records its true
* creation time. */
if (c->meta[pack].created_time == 0) {
c->meta[pack].magic = 0x424C4B5F5354524BULL;
c->meta[pack].created_time = now;
}
@@ -1135,6 +1153,11 @@ int blk_meta_zone_read(uint32_t devblock_from_top, uint8_t buf[4096]) {
blk_dev_slot_t *slot = first_disk_slot();
if (!slot || !slot->dev) return BLK_ENODEV;
if (devblock_from_top >= slot->vol_meta.meta_fence_blocks) return BLK_EINVAL;
/* The fence index must also sit inside the physical device -- a corrupt
* volume whose meta_fence_blocks >= total_devblocks would otherwise pass
* the fence check and let (total_devblocks - 1 - devblock_from_top)
* underflow on the unsigned subtraction below, targeting a wild devblock. */
if ((uint64_t) devblock_from_top >= slot->vol_meta.total_devblocks) return BLK_EINVAL;
uint64_t devblock_idx = slot->vol_meta.total_devblocks - 1ULL - devblock_from_top;
uint32_t base1k = (uint32_t) devblock_idx * 4u;
@@ -1150,6 +1173,10 @@ int blk_meta_zone_write(uint32_t devblock_from_top, const uint8_t buf[4096]) {
blk_dev_slot_t *slot = first_disk_slot();
if (!slot || !slot->dev) return BLK_ENODEV;
if (devblock_from_top >= slot->vol_meta.meta_fence_blocks) return BLK_EINVAL;
/* Same physical-bounds guard as blk_meta_zone_read(): prevents the
* unsigned underflow of (total_devblocks - 1 - devblock_from_top) on a
* corrupt fence >= device size. */
if ((uint64_t) devblock_from_top >= slot->vol_meta.total_devblocks) return BLK_EINVAL;
uint64_t devblock_idx = slot->vol_meta.total_devblocks - 1ULL - devblock_from_top;
uint32_t base1k = (uint32_t) devblock_idx * 4u;