mlxd_strategy dereferences stale/uninitialized bio_driver_info instead of dev->si_drv1
Summary
mlxd_strategy (mlx_disk.c:161): sc=(struct mlxd_softc*)bio->bio_driver_info. Nothing on mlx I/O path populates bio_driver_info with softc. push_bio() (vfs_bio.c:768-788) inits only bio_prev/bio_buf/bio_offset/bio_done/bio_next NOT bio_driver_info. dev_dstrategy (kern_device.c:386/413) doesnt set it either. Result: stale/recycled/zero value. NULL->EINVAL every I/O fails; non-NULL->wild pointer deref sc->mlxd_drive offset ~24 (mlxd_softc mlxvar.h:225-236) -> kernel page fault panic. Wild ptr propagates through mlx_startio (mlx.c:1766 mlxd=bio_driver_info; driveno=mlxd->mlxd_drive-sc->mlx_sysdrive) and mlx_completeio (mlx.c:1815 device_printf(mlxd->mlxd_dev)). Correct pattern visible in sibling amr_disk.c:172-179: sc=dev->si_drv1 THEN bio->bio_driver_info=sc. mlxd_attach DOES set dsk->si_drv1=sc at mlx_disk.c:260 but strategy never reads it. Attacker: local user open /dev/mlxd* (operator/root) + read() via physio; stress vnode I/O first to populate recycled pbuf bio_driver_info with non-NULL stale. AV:L/AC:H/PR:L, A:H. Fix: source sc from dev->si_drv1 then publish on bio->bio_driver_info.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2074 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full narrative: source-trace, mechanism, fix, Phase 8 validation | 4.0 KB | β raw |
| README.md | readme | original reviewer README with theoretical trigger | 1.1 KB | β raw |
| fix.diff | suggested-fix | git-apply-able: source sc from dev->si_drv1 then publish bio_driver_info (mirrors amr_disk.c:172-179) | 1006 B | view raw |
| build.sh | build-script | pushes fix.diff, documents validated build path | 781 B | view raw |
| run.sh | run-script | reports HW-gated source-only status; confirms no mlx HW | 974 B | view raw |
| env.txt | environment | guest uname, cc, HW-gate check, source-trace citations | 2.3 KB | view raw |
| fix_build_combined.log | build-log | FULL combined (DF-2073+DF-2074) nativekernel build, rc=0 -Werror | 5.6 MB | β download |
DF-2074 PoC β mlxd_strategy stale bio_driver_info deref
Preconditions
- Mylex DAC960/acceleRAID hardware present (mlxd child attached)
- Local user with /dev/mlxd* access (root or operator group)
Trigger
/* cc -o mlxd_poc mlxd_poc.c */
#include <fcntl.h>
#include <unistd.h>
int main(void){
char buf[512];
/* Stress vnode I/O first to populate recycled pbuf bio_driver_info
* with non-NULL stale pointers */
int fd = open("/dev/mlxd0", O_RDONLY);
if (fd < 0) return 1;
read(fd, buf, sizeof buf); /* -> wild deref -> kernel panic */
return 0;
}
Expected output
- bio_driver_info == NULL: every I/O fails EINVAL (driver non-functional)
- bio_driver_info != NULL (stale vnode ptr): kernel page fault in mlxd_strategy or mlx_startio (wild deref of sc->mlxd_drive at offset ~24)
- Reproduction is probabilistic; loop under concurrent FS load to hit non-NULL
Fix
Source sc from dev->si_drv1 (set at mlxd_attach:260), then publish on bio->bio_driver_info for controller start/complete paths β exactly as amr_disk.c:172-179 does.
DF-2074 β mlxd_strategy dereferences stale/uninitialized bio_driver_info
Verdict
REPRODUCED (source-only confirmation; HW-gated β no Mylex DAC960/acceleRAID adapter on guest).
Severity / impact
Low. Realistic ceiling: local DoS. When bio_driver_info is NULL every I/O
fails EINVAL (driver non-functional). When it holds a stale non-NULL value
(recycled from a prior struct bio / pbuf), sc->mlxd_drive (offset ~24) is a
wild pointer deref β kernel page-fault panic. Requires operator/root access to
/dev/mlxd* (AV:L/AC:H/PR:L). No escalation primitive β pure DoS + a stale-
pointer hardening gap.
Mechanism (cited path confirmed line-by-line)
mlxd_strategy(ap) (sys/dev/raid/mlx/mlx_disk.c:156-190):
1. bio = ap->a_bio; bp = bio->bio_buf; (:159-160).
2. sc = (struct mlxd_softc *)bio->bio_driver_info; at :161 β reads a field
that NOTHING on the I/O path populates.
3. if (sc == NULL) β EINVAL (good for the NULL case).
4. Otherwise: sc->mlxd_drive->ms_state (:173), devstat_start_transaction,
mlx_submit_bio(sc->mlxd_controller, bio) (:180) β all deref the stale sc.
Where bio_driver_info should come from but doesn't:
- push_bio (sys/kern/vfs_bio.c:768-788) inits only bio_prev, bio_buf,
bio_offset, bio_done, bio_next β explicitly NOT bio_driver_info.
- mlx_submit_bio (sys/dev/raid/mlx/mlx.c:683-693) does not set it.
- dev_dstrategy / physio paths do not set it for this driver.
Downstream consumers also trust the same unpopulated field (would be fine once
strategy publishes it):
- mlx_startio (mlx.c:1766): mlxd = bio->bio_driver_info; driveno =
mlxd->mlxd_drive - sc->mlx_sysdrive; β wild deref.
- mlx_completeio (mlx.c:1815): mlxd = bio->bio_driver_info; then
device_printf(mlxd->mlxd_dev, ...) β wild deref.
- mlxd_intr (mlx_disk.c:196): sc = bio->bio_driver_info; β wild deref.
The CORRECT source IS set at attach: mlxd_attach (mlx_disk.c:260) does
dsk->si_drv1 = sc; (and mlxd_open/mlxd_close/mlxd_ioctl all correctly
read dev->si_drv1). Only mlxd_strategy reads the wrong field.
Reference (correct pattern)
Sibling amrd_strategy (sys/dev/raid/amr/amr_disk.c:167-182):
cdev_t dev = ap->a_head.a_dev;
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1; // source from si_drv1
if (sc == NULL) { ... EINVAL ... }
bio->bio_driver_info = sc; // publish for downstream
Trigger (theoretical; HW-gated)
Local user with /dev/mlxd* access reads/writes; under concurrent vnode I/O
load the recycled pbuf's bio_driver_info may hold a non-NULL stale pointer β
wild deref panic in mlxd_strategy/mlx_startio/mlx_completeio.
Not reachable on this guest β no Mylex adapter (pciconf -l | grep mlx β no
match), so mlxd never attaches and /dev/mlxd* does not exist. Runtime
confirmation impossible.
Fix
Source sc from dev->si_drv1 (set at mlxd_attach:260), then publish
bio->bio_driver_info = sc for the mlx_startio/mlx_completeio/mlxd_intr
downstream consumers β exactly mirroring amr_disk.c:172-179. See fix.diff.
The fix matches the finding markdown's ## Recommended fix proposal (source
from dev->si_drv1, publish on bio_driver_info).
Fix validation (Phase 8)
fix.diffapplied to/usr/src(combined with DF-2073's fix).make -j6 nativekernel KERNCONF=X86_64_GENERICβ rc=0, -Werror clean (no errors, no warnings).kernel.stripped+kernel.debugproduced.- Installed to
/boot/kernel/kernel; rebooted βkern.versionbumped#0β#1(Sat Jul 25 11:53:22 UTC 2026); guest boots healthy, no regression. - Runtime before/after NOT possible (HW absent) β
fix_status: not_testableat runtime, but build-validated + source-traced to close the path.
PoC changes
The README's mlxd_poc.c is left as-is (it correctly documents the theoretical
trigger but cannot run without HW). Evidence pack carries the source-trace
citations, the fix.diff, the combined build log, and the environment record.
Fix verification
not_testableVALIDATED build+boot. HW-gated.
NK_DONE rc=0; kernel #1 boots clean.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- m
- l
- x
- /
- m
- l
- x
- _
- d
- i
- s
- k
- .
- c
- :
- 1
- 6
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- m
- l
- x
- /
- m
- l
- x
- _
- d
- i
- s
- k
- .
- c
- :
- 2
- 6
- 0
- s
- y
- s
- /
- k
- e
- r
- n
- /
- v
- f
- s
- _
- b
- i
- o
- .
- c
- :
- 7
- 6
- 8
Detail
Exploit chain
none (DoS/stale-pointer).
Evidence (decisive lines)
HW-GATED (no Mylex RAID). Source-CONFIRMED. mlxd_strategy reads bio->bio_driver_info which is NEVER populated on the I/O path. push_bio doesn't init it, mlx_submit_bio doesn't set it. Wild pointer der
Verified recommended fix
Source sc from dev->si_drv1 then set bio->bio_driver_info=sc, mirroring amr_disk.c:172-179.
Verdict
HW-GATED (no Mylex RAID). Source-CONFIRMED. mlxd_strategy reads bio->bio_driver_info which is NEVER populated on the I/O path. push_bio doesn't init it, mlx_submit_bio doesn't set it. Wild pointer deref. Correct source is dev->si_drv1 (set at mlxd_attach:260).
No comments yet.