MLX_NEXT_CHILD uses mlx_sysdrive[0] instead of the found [i] -> device_get_unit(NULL) panic after drive 0 is detached
Summary
MLX_NEXT_CHILD with *arg==-1 reads the unit from mlx_sysdrive[0].ms_disk inside the loop that verified mlx_sysdrive[i].ms_disk != NULL - a [0]-vs-[i] copy-paste bug. DETACH_DRIVE on system drive 0 leaves [0].ms_disk==NULL while later drives stay attached; the next NEXT_CHILD(-1) calls device_get_unit(NULL) -> NULL deref panic. Operator/root only; management tooling enumerating drives after a detach. One-token fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3949 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | doc | bug + reachability + fix | 1.8 KB | β raw |
| VERDICT.md | verdict | source proof | 1.7 KB | β raw |
| mlx_ioctl_trigger.c | trigger | DETACH drive0 then NEXT_CHILD(-1) | 4.6 KB | view raw |
| build.sh | script | build | 108 B | view raw |
| run.sh | script | run | 120 B | view raw |
| build.log | log | guest compile + run | 356 B | view raw |
| run.log | log | guest run (ENOENT, no HBA) | 217 B | view raw |
| env.txt | env | guest uname | 185 B | view raw |
| fix.diff | suggested-fix | [0] -> [i] | 446 B | view raw |
DF-3949 β MLX_NEXT_CHILD NULL dereference (mlx_sysdrive[0] vs [i])
Severity: Low Β· CWE-476 Β· operator/root gated (/dev/mlxN is 0600
root:operator, mlx.c:466-468)
Bug
sys/dev/raid/mlx/mlx.c:746-761 β MLX_NEXT_CHILD walks the attached system
drives. When the caller asks for the first child (*arg == -1) the code
takes the unit number from sc->mlx_sysdrive[0].ms_disk instead of the
drive it just found (sc->mlx_sysdrive[i].ms_disk):
for (i = 0; i < MLX_MAXDRIVES; i++) {
if (sc->mlx_sysdrive[i].ms_disk != NULL) {
if (*arg == -1) {
*arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk); /* [0]! */
return(0);
If system drive 0 has no child while a later drive does, this calls
device_get_unit(NULL) β return(dev->unit) (sys/kern/subr_bus.c:1646-1649)
β NULL dereference β kernel panic.
Reachability
mlx_startup() only creates children sequentially up to the
sd_size == 0xffffffff sentinel, so on a plain boot [0] is attached
whenever any later drive is. The state is produced by MLX_DETACH_DRIVE on
system drive 0 (mlx.c:774-804): mlx_findunit() locates drive 0, the child
is deleted and dr->ms_disk = NULL (mlx.c:796), while drive 1+ stays
attached. The very next MLX_NEXT_CHILD(-1) β e.g. from mlxcontrol-style
management tooling enumerating drives after a detach β panics the kernel.
(RESCAN_DRIVES after config changes can also produce holes.)
Build / Run / Expected
cc -O2 -Wall -o mlx_ioctl_trigger mlx_ioctl_trigger.c
./mlx_ioctl_trigger nextchild /dev/mlx0 # detach drive0, then NEXT_CHILD(-1)
Expected on hardware: immediate kernel panic in device_get_unit
(NULL+offset read). On this audit guest: ENOENT (no DAC960 HBA) β
hardware-gated.
Fix
Use the drive actually found: device_get_unit(sc->mlx_sysdrive[i].ms_disk).
DF-3949 β Verification Verdict
Status: inconclusive (hardware-gated; source-certain) Impact demonstrated: none (expected: kernel panic) Confidence: certain (source), runtime untestable (no Mylex DAC960 HBA in QEMU guest)
Source proof
- Copy-paste index bug:
sys/dev/raid/mlx/mlx.c:753*arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk);inside the loop that just verifiedsc->mlx_sysdrive[i].ms_disk != NULL(mlx.c:750). Should be[i]. - State producer: MLX_DETACH_DRIVE (mlx.c:774-804) deletes the child of the
requested drive and sets
dr->ms_disk = NULL(mlx.c:796); drive 0 can be detached while later drives remain attached. device_get_unitis a direct deref:sys/kern/subr_bus.c:1646-1649int device_get_unit(device_t dev) { return(dev->unit); }β NULL+offset read β kernel page fault.- Privilege:
/dev/mlxNcreatedUID_ROOT, GID_OPERATOR, S_IRUSR|S_IWUSR(mlx.c:466-468) β operator/root only β Low severity.
Guest result
Trigger compile-tested on DragonFly 6.5-DEVELOPMENT guest (BUILD_RC=0); runtime exits ENOENT β no DAC960 controller present. Honest classification: inconclusive, not false-positive (the path is fully live source; every hop cited).
Fix validation
fix.diff: one-token change [0] β [i]. Not runtime-testable on this guest
(no HBA) β fix_status: not_testable.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablenot_testable: no Mylex hardware in guest; one-token fix
fix.diff
Confirmed kernel references
Detail
Exploit chain
operator/root: ioctl(MLX_DETACH_DRIVE, drive0_unit) then ioctl(MLX_NEXT_CHILD, -1) -> device_get_unit(NULL) -> panic
Evidence (decisive lines)
README/VERDICT cite mlx.c:750-758,774-796 + subr_bus.c:1646; mlx_ioctl_trigger.c (nextchild mode); build.log/run.log; fix.diff [0]->[i]
PoC changes
no seed; self-contained trigger with mlxio.h structs inlined so it builds on stock guest
Verified recommended fix
device_get_unit(sc->mlx_sysdrive[i].ms_disk) instead of [0].
Verdict
Source-certain NULL-deref: MLX_NEXT_CHILD handler indexes mlx_sysdrive[0] instead of the found [i] (mlx.c:753). After MLX_DETACH_DRIVE removes system drive 0's child (mlx.c:774-796 leaves ms_disk==NULL) while a later drive stays attached, MLX_NEXT_CHILD(-1) calls device_get_unit(NULL) -> return(dev->unit) (subr_bus.c:1646-1649) -> kernel panic. Root/operator gated (make_dev 0600 root:operator, mlx.c:466-468). Runtime inconclusive: no Mylex DAC960 HBA in the QEMU guest; trigger compile-tested on guest (BUILD_RC=0), run exits ENOENT.
No comments yet.