From 3c053f02a6dfeb9fb9630eb9933687d76adfaea5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Nov 2026 18:07:23 +0000 Subject: [PATCH] fix(v4): the build refuses an Artemis cap its 32-bit block numbers cannot hold (#182) sk_v4_grow.c's dev_rw4k computes 1 KiB block numbers as (uint32_t)devblock * 4, which wraps past 4 TiB; Artemis is capped at 256 GiB (STICK_ARTEMIS_CAP_GIB, kernel/Makefile), so it is latent -- but nothing said so where the cast is. Now a compile-time check there (the C99 negative-array pattern the tree uses): a cap past 4 TiB stops the build, and a cap not defined at all is an #error, not a skipped check. gpt.c's casts are bounded by the disk's own size, itself a 32-bit count of 1 KiB blocks, checked before each: each now says so. kernel/tests' check_cap, seen to fail first (a 4096 GiB cap built); now 3 checks, 0 failures -- 256 GiB builds, 4096 GiB and none refused. Built for amd64, aarch64, riscv64. The plan's increments 2 and 3 marked. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm --- docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md | 11 +++++++---- kernel/src/gpt.c | 5 +++-- kernel/src/v4/sk_v4_grow.c | 8 ++++++++ kernel/tests/Makefile | 15 +++++++++++++++ logs/20261112-180723/kernel-tests/check_cap.log | 3 +++ 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 logs/20261112-180723/kernel-tests/check_cap.log diff --git a/docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md b/docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md index 65a604a4..238eb230 100644 --- a/docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md +++ b/docs/v4.0.0/plans/2026-11-12-kernel-host-tests.md @@ -29,9 +29,12 @@ fails half-way, a stop that spins) is still tested first and seen to fail. v4 ha entry out of range past `max` (#180); the first write failing and a later one failing (#168). Seen to fail against `gpt.c` as it was before #168 and #180 (`54311be1~1`): 12 checks, 4 failures -- each of the two defects caught. Now: 0 failures. -- [ ] 2. The xHCI configuration-descriptor walk moved out of the event loop, then #170 and #171 - tested first and fixed. -- [ ] 3. #176 (seeds wiped on every exit), #177 (the fatal stops halt), #178 (the slot array) -- - each with what it needs moved or faked. +- [x] 2. The xHCI configuration-descriptor walk moved out of the event loop (`usb_config.c`, + `test_usb_config`), then #170 and #171 tested first (10 failures against the old decision) + and fixed. +- [ ] 3. #176 (seeds wiped on every exit) -- the approach to rule. [x] #177 (the fatal stops halt: + `sk_v4_stop.c`, `test_sk_v4_stop`, a spin seen failing). [x] #178 (the slot array: + `sk_v4_slots.c`, `test_sk_v4_slots`, 4 failures against the old allocation). [x] #182 (the + cap checked at compile time: `check_cap`, a 4 TiB cap seen building before). - [ ] 4. #179 once #187 is ruled (the block layer's header read). - [ ] 5. To rule: the kernel tests as a protocol stage and in the gate. diff --git a/kernel/src/gpt.c b/kernel/src/gpt.c index 12b47ede..e154206e 100644 --- a/kernel/src/gpt.c +++ b/kernel/src/gpt.c @@ -118,6 +118,7 @@ int gpt_read(blkio_dev_t *disk, gpt_part_t *parts, unsigned max, unsigned *count if (ent_lba + (ebytes + GPT_SECTOR - 1u) / GPT_SECTOR > first_usable) return GPT_BAD; /* the entry array, a 1 KiB block at a time */ + /* #182: the entries' block numbers are 32 bits -- ent_lba < disk_sectors, checked above */ for (i = 0; (size_t)i * BLKIO_FORTH_BLOCK_SIZE < ebytes; i++) { if (blkio_read(disk, (uint32_t)(ent_lba / 2u) + i, g_gpt_entries + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) return GPT_BAD; @@ -172,7 +173,7 @@ static int put_sectors(blkio_dev_t *disk, uint64_t lba, const uint8_t *src, uint uint32_t i; for (i = 0; i < n; i++) { uint64_t s = lba + i; - uint32_t blk = (uint32_t)(s / 2u); + uint32_t blk = (uint32_t)(s / 2u); /* #182: s < the disk's sectors, a uint32 count of blocks x 2 */ if (blkio_read(disk, blk, g_gpt_rmw) != BLKIO_OK) return -1; memcpy(g_gpt_rmw + (s % 2u) * GPT_SECTOR, src + (size_t)i * GPT_SECTOR, GPT_SECTOR); if (blkio_write(disk, blk, g_gpt_rmw) != BLKIO_OK) return -1; @@ -424,7 +425,7 @@ int gpt_part_open(blkio_dev_t *dev, gpt_part_dev_t *st, blkio_dev_t *disk, const if ((p->first_lba % 2u) != 0u || (sectors % 2u) != 0u) return BLKIO_EINVAL; if (p->first_lba / 2u + sectors / 2u > disk->total_blocks) return BLKIO_EINVAL; st->disk = disk; - st->first_fblock = (uint32_t)(p->first_lba / 2u); + st->first_fblock = (uint32_t)(p->first_lba / 2u); /* #182: checked against total_blocks just above */ st->fblocks = (uint32_t)(sectors / 2u); params.forth_block_size = BLKIO_FORTH_BLOCK_SIZE; params.total_blocks = st->fblocks; diff --git a/kernel/src/v4/sk_v4_grow.c b/kernel/src/v4/sk_v4_grow.c index 450b619f..5c34aa39 100644 --- a/kernel/src/v4/sk_v4_grow.c +++ b/kernel/src/v4/sk_v4_grow.c @@ -26,6 +26,14 @@ static union { } g_hdr; static uint8_t g_move[4096]; +/* #182: the 1 KiB block numbers below are 32 bits ((uint32_t)devblock * 4u, as the block layer's + * are): enough for 4 TiB, and Artemis is capped at STICK_ARTEMIS_CAP_GIB (kernel/Makefile, 256 GiB). + * A cap past 4 TiB stops the build here instead of wrapping the block numbers. */ +#ifndef STICK_ARTEMIS_CAP_GIB +#error "STICK_ARTEMIS_CAP_GIB (kernel/Makefile) must be defined: the check below depends on it" +#endif +typedef char sk_v4_grow_cap_fits_32_bit_blocks[(STICK_ARTEMIS_CAP_GIB * 1024ULL * 1024ULL <= 0xFFFFFFFFULL) ? 1 : -1]; + static int dev_rw4k(blkio_dev_t *dev, uint64_t devblock, uint8_t *buf, int write) { uint32_t base = (uint32_t)devblock * 4u, k; diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile index 973c192e..616e12e7 100644 --- a/kernel/tests/Makefile +++ b/kernel/tests/Makefile @@ -40,3 +40,18 @@ $(foreach t,$(TESTS),$(eval $(call TEST_RULE,$(t)))) .PHONY: test test: $(addprefix run-,$(TESTS)) @echo "all kernel host tests passed" + +# #182: sk_v4_grow.c's 32-bit block arithmetic holds while Artemis is capped below 4 TiB -- the build +# itself refuses a cap past that. Seen here: 256 GiB (the real cap) compiles, 4096 GiB must not. +.PHONY: run-check_cap +run-check_cap: + @echo "check_cap" + @$(CC) $(CFLAGS) -I$(ROOT)/v4/include -DSTICK_ARTEMIS_CAP_GIB=256ULL -c $(ROOT)/kernel/src/v4/sk_v4_grow.c -o /dev/null \ + || { echo "FAIL #182: sk_v4_grow.c does not build with the real cap, 256 GiB"; exit 1; } + @if $(CC) $(CFLAGS) -I$(ROOT)/v4/include -DSTICK_ARTEMIS_CAP_GIB=4096ULL -c $(ROOT)/kernel/src/v4/sk_v4_grow.c -o /dev/null 2>/dev/null; then \ + echo "FAIL #182: a 4096 GiB cap builds -- its block numbers do not fit 32 bits, and nothing stops it"; exit 1; fi + @if $(CC) $(CFLAGS) -I$(ROOT)/v4/include -c $(ROOT)/kernel/src/v4/sk_v4_grow.c -o /dev/null 2>/dev/null; then \ + echo "FAIL #182: no cap defined builds -- the check would be skipped, not failed"; exit 1; fi + @echo " 3 checks, 0 failures" + +test: run-check_cap diff --git a/logs/20261112-180723/kernel-tests/check_cap.log b/logs/20261112-180723/kernel-tests/check_cap.log new file mode 100644 index 00000000..25ac570f --- /dev/null +++ b/logs/20261112-180723/kernel-tests/check_cap.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13aa0fa641f9347dd1c2c28f341bd4c172b0a4c93c3729b448a6143351f9fb79 +size 248