β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2074

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2074 Β· 7 files
FileTypeDescriptionSize
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
README.md readme original reviewer README with theoretical trigger
↓ download raw

DF-2074 PoC β€” mlxd_strategy stale bio_driver_info deref

Preconditions

  1. Mylex DAC960/acceleRAID hardware present (mlxd child attached)
  2. 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.

VERDICT.md verdict full narrative: source-trace, mechanism, fix, Phase 8 validation
↓ download raw

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.diff applied 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.debug produced.
  • Installed to /boot/kernel/kernel; rebooted β†’ kern.version bumped #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_testable at 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_testable
baseline no→ patch + rebuild →patched clean

VALIDATED build+boot. HW-gated.

NK_DONE rc=0; kernel #1 boots clean.
↓ fix.diffcombined kernel #1 rc=0 -Werror

Confirmed kernel references

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).