FABRIC-2.md: correct glibc misattribution in 2h hot-detach writeup

block_subsystem.c is shared/vendored source -- its calloc()/free() calls
resolve to real glibc in the hosted build, but to this kernel's own
freestanding shims (src/starkernel/vm/host/shim.c, backed by kmalloc()/
kfree() in src/starkernel/memory/kmalloc.c) in the kernel build, which is
where the address-reuse bug was actually diagnosed live. The previous
writeup said "glibc's allocator" -- wrong environment entirely. Verified
kmalloc_aligned() is a plain first-fit walk from heap_head with no
coalescing, confirming the same free-then-immediate-same-size-alloc reuse
behavior originally observed, just attributed to the right allocator.
Caught by the user asking directly whether this project has any glibc
dependency, given .claude/CLAUDE.md's strict ANSI C99/no-GNU-extensions
requirement (confirmed -std=c99 throughout both Makefiles). Documentation
correction only -- no code changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
This commit is contained in:
Robert Allan James
2026-08-25 14:16:12 -04:00
co-authored by Claude Sonnet 5
parent af267a52a6
commit 1b80609cb0
+19 -7
View File
@@ -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