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

mpt_raid_attach error paths: MPT_UNLOCK of a never-acquired lock (deterministic lockmgr panic) plus csa CCB leak

Summary

If mpt_spawn_raid_thread fails, goto cleanup reaches MPT_UNLOCK although MPT_LOCK is only taken later - lockmgr panics 'LK_RELEASE: no lock held'. On the other error path the csa CCB is never freed. Attach-time kthread ENOMEM with an MPT HBA present: recoverable ENOMEM becomes a kernel panic. Return-directly + free-ccb fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4212 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.2 KB ↓ raw
VERDICT.md β€” 1.5 KB ↓ raw
env.txt β€” 992 B view raw
fix.diff β€” 675 B view raw
manifest.json β€” 647 B view raw
verdict.json β€” 1.9 KB view raw

DF-4212 β€” mpt_raid_attach error paths: MPT_UNLOCK on a lock it never took (guaranteed panic) + CCB leak

  • File: sys/dev/disk/mpt/mpt_raid.c
  • Lines: 283–287, 288, 311, 314–317
  • Class: error-path lock protocol violation (CWE-667) β†’ kernel panic; memory leak (CWE-401)
  • Hardware gate: LSI MPT Fusion RAID controller, attach-time error (kthread_create ENOMEM)

Bug 1 β€” unlock without lock (panic)

mpt_raid_attach():

    error = mpt_spawn_raid_thread(mpt);          /* 283 */
    if (error != 0) {
        mpt_prt(mpt, "Unable to spawn RAID thread!\n");
        goto cleanup;                        /* 286: MPT_LOCK NOT held */
    }
    csa = &xpt_alloc_ccb()->csa;                 /* 288 */
    MPT_LOCK(mpt);                               /* 290 */
    ...
cleanup:                                             /* 314 */
    MPT_UNLOCK(mpt);                             /* 315: LK_RELEASE */
    mpt_raid_detach(mpt);
    return (error);

If mpt_spawn_raid_thread() fails (kthread_create ENOMEM, mpt_raid.c:645), control reaches cleanup without ever executing MPT_LOCK (taken only at line 290). MPT_UNLOCK is lockmgr(&mpt->mpt_lock, LK_RELEASE) (sys/dev/disk/mpt/mpt.h:763-764), and DragonFly's lockmgr panics on releasing an unheld lock: panic("lockmgr: LK_RELEASE: no lock held") (sys/kern/kern_lock.c:761-768). So the error path converts a recoverable ENOMEM into a guaranteed kernel panic during device attach (boot or kldload under memory pressure).

Bug 2 β€” CCB leak on the other error path

If mpt_register_handler fails (line 292–297), the csa CCB allocated at line 288 is never freed β€” cleanup only calls mpt_raid_detach(), which allocates and frees its own CCB (mpt_raid.c:333, 349). One xpt_alloc_ccb() leaks per failed attach.

Verification status

untested β€” hardware-gated (no MPT HBA on the guest, see env.txt; also requires kthread_create failure). Source-certain: the lock-state arithmetic is two lines apart and kern_lock.c:768 makes the panic deterministic.

Fix

fix.diff: return directly on spawn failure (nothing to unwind); free csa before calling mpt_raid_detach on the handler-registration failure.

VERDICT.md
↓ download raw

VERDICT β€” DF-4212

status: untested (hardware-gated) Β· impact: panic (attach-time, OOM-gated) Β· confidence: certain (source)

Not reproducible on the audit guest: no MPT Fusion HBA (env.txt), and the trigger additionally needs kthread_create() to fail during attach.

Source proof: - MPT_LOCK is taken at mpt_raid.c:290, after the spawn-failure goto cleanup at 283–287; cleanup (314–317) executes MPT_UNLOCK unconditionally. - MPT_LOCK/MPT_UNLOCK are plain lockmgr EXCLUSIVE/RELEASE (sys/dev/disk/mpt/mpt.h:763-764), non-recursive, and DragonFly lockmgr hard-panics on LK_RELEASE with no lock held (sys/kern/kern_lock.c:761-768: panic("lockmgr: LK_RELEASE: no lock held")). - CCB leak: csa allocated at mpt_raid.c:288 is freed only at line 311 on the success path; the goto cleanup at 296 skips it, and mpt_raid_detach frees only its own CCB (mpt_raid.c:333/349).

Deterministic panic, but the precondition (attach-time kthread ENOMEM on a machine with the RAID HBA) makes it Low severity. Fix is trivial and low-risk; not runtime-validated (no hardware) β†’ 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_testable
baseline no→ patch + rebuild →patched clean

diff authored and git-apply-verified against the read-only sys/ tree; runtime fix validation impossible without the triggering hardware on the QEMU guest

findings/poc/DF-4212/fix.diff (git apply --check passed)
↓ fix.diffper-fix-DF-4212

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4212/VERDICT.md, findings/poc/DF-4212/env.txt (guest pciconf: no triggering hardware), findings/poc/DF-4212/fix.diff

PoC changes

no runnable PoC: hardware-gated path absent from the audit guest (env.txt)

Verified recommended fix

return early on spawn failure (nothing to unwind); free csa on the register-failure path

Verdict

mpt_raid_attach takes MPT_LOCK only after mpt_spawn_raid_thread succeeds (mpt_raid.c:290), but the spawn-failure goto cleanup (283-287) reaches MPT_UNLOCK (315) on a lock never acquired -> DragonFly lockmgr panic 'LK_RELEASE: no lock held' (kern_lock.c:768). The other error path (handler registration failure, 292-297) leaks the csa CCB allocated at 288. Both are attach-time error-path defects; hardware-gated (no MPT HBA on guest).