diff --git a/FABRIC-2.md b/FABRIC-2.md index fc2326c..b5d0d99 100644 --- a/FABRIC-2.md +++ b/FABRIC-2.md @@ -3883,13 +3883,25 @@ current tail — and recommended a pointer-identity check in `blk_vm_load()`'s c a minimal fix requiring no new API. That fix was implemented, then **directly falsified by its own designed-for-this test**: attach device A (blank), detach, re-attach device B (distinct content) at the identical LBN range, read that LBN — pointer comparison passed the check (i.e. -called it a hit) and served **stale content from device A** anyway. Root cause: glibc's -allocator hands `free(slot)` in `blk_subsys_detach_device()` straight back to the very next -same-size `calloc(1, sizeof(*slot))` in `blk_subsys_attach_device()`, with nothing else allocated -in between — confirmed live in this exact session, not inferred — so the "fresh" pointer and the -stale one were bitwise identical despite belonging to two different physical devices. A pointer -comparison cannot distinguish "still the same live device" from "a different device that -happened to land at the same address" when the allocator is this deterministic. +called it a hit) and served **stale content from device A** anyway. Root cause: this kernel's own +freestanding heap allocator — `block_subsystem.c` is shared/vendored source, so its `calloc()`/ +`free()` calls resolve differently per build (real glibc in the hosted build; this kernel's own +`malloc`/`free`/`calloc` shims in `src/starkernel/vm/host/shim.c`, backed by `kmalloc()`/ +`kfree()` in `src/starkernel/memory/kmalloc.c`, in the kernel build this bug was actually diagnosed +in). `kmalloc_aligned()` is a plain first-fit walk from `heap_head` on every call; `kfree()` just +marks a block's `free` flag — no coalescing, no randomization. `free(slot)` in +`blk_subsys_detach_device()` hands its block straight back to the very next same-size +`calloc(1, sizeof(*slot))` in `blk_subsys_attach_device()`, with nothing else allocated in +between — confirmed live in this exact session, not inferred — so the "fresh" pointer and the +stale one were bitwise identical despite belonging to two different physical devices. (An initial +draft of this writeup attributed the reuse to "glibc's allocator" — wrong: the live diagnosis ran +in the kernel build, nowhere near glibc; corrected after the user asked directly whether this +project has any glibc dependency, per `.claude/CLAUDE.md`'s strict ANSI C99/no-GNU-extensions +requirement, which the kernel build already honors throughout — this was a documentation +attribution error, not a code or license-compliance one.) A pointer comparison cannot distinguish +"still the same live device" from "a different device that happened to land at the same address" +when the allocator is this deterministic — true of this first-fit kmalloc just as much as it +would be of glibc's own allocator under the same free-then-immediately-realloc pattern. Fixed properly with a monotonic `blk_subsys_epoch()` counter (`block_subsystem.c`, `uint64_t g.epoch`, bumped in `blk_subsys_attach_device()`, `blk_subsys_add_raw_device()`, and