midi_init() error paths: lockuninit() on held locks (KKASSERT panic) and m->synth leaked on every destruction path
Summary
err2 is reached with m->lock and m->qlock held and immediately lockuninits both - kern_lock KKASSERTs the lock is unheld -> attach-time panic on MPU-driver init failure. Additionally m->synth is never freed: err1 kfrees only m, and midi_destroy also leaks it on every successful teardown. Release-before-uninit + free-synth fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4285 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| fix.diff | β | 572 B | view raw | |
| manifest.json | β | 756 B | view raw | |
| verdict.json | β | 2.3 KB | view raw |
DF-4285 β midi_init() error paths: lockuninit() on held locks (KKASSERT panic) + m->synth leak
What
Two defects on the error paths of midi_init()
[sys/dev/sound/midi/midi.c:273-385]:
-
lockuninit on held locks β panic.
m->lockandm->qlockare acquired at midi.c:325-326 before the failure points that jump toerr2(theMIDIQ_BUFsanity check at 343-345 and, realistically,MPU_INIT(m, cookie)failure at 354-355).err2[371-373] then callslockuninit(&m->qlock); lockuninit(&m->lock)while both locks are still held. DragonFly'slockuninit()KKASSERTs the lock is not held [sys/kern/kern_lock.c:1325-1336:KKASSERT((count & (LKC_EXREQ|LKC_UPREQ)) == 0 && ((count & LKC_SHARED) || (count & LKC_SMASK) == 0))] β kernel panic on an MPU-driver init failure at attach time (e.g. flaky emu10kx UART). -
m->synth leak.
m->synthis allocated at midi.c:311, but every destruction/error path frees onlym: err2 β err1kfree(m, M_MIDI)[380], and β beyond the init path βmidi_destroy()[1432-1436] also never freesm->synth. Every failed init and every successfulmidi_uninit/midi_destroyleakssizeof(struct synth_midi)+ kobj bookkeeping.
Trigger
Driver-internal error path: requires a midi-registering driver whose MPU_INIT fails at attach (in-tree: snd_emu10kx) for (1); any midi device destroy for (2). Not triggerable on the audit guest (no such hardware). Source-certain; hardware-gated β filed Low.
Fix
fix.diff β release both locks before lockuninit on err2; free m->synth
on err1 (covers err2 fallthrough as well).
No runtime PoC exists for this finding (error path is hardware-gated); pack documents the source-level proof.
VERDICT β DF-4285
Status: inconclusive (source-certain defect; error path unreachable on this guest) Impact: attach-time kernel panic (KKASSERT in lockuninit) + small kernel heap leak Confidence: certain (defect), speculative (real-world trigger)
Proof chain
- midi.c:325-326:
lockmgr(&m->lock, LK_EXCLUSIVE); lockmgr(&m->qlock, LK_EXCLUSIVE); - midi.c:343-345 and 354-355:
goto err2reached with both locks held (the MIDIQ_BUF check 343-345 is effectively unreachable with M_WAITOK allocations, butMPU_INIT(m, cookie)failure at 354 is a normal hardware-driver error return). - midi.c:371-373 (err2):
lockuninit(&m->qlock); lockuninit(&m->lock); - sys/kern/kern_lock.c:1325-1336:
lockuninit()containsKKASSERT((count & (LKC_EXREQ | LKC_UPREQ)) == 0 && ((count & LKC_SHARED) || (count & LKC_SMASK) == 0))β a held (exclusive) lock has LKC_SMASK owner bits set β assertion fails β panic. - Leak: midi.c:311
m->synth = kmalloc(...); freed nowhere β err1kfree(m, M_MIDI)[380] andmidi_destroy()[1432-1436] free onlym(grep confirms nokfree(m->synth)anywhere in the tree).
Why not runtime-verified
The only in-tree midi_init caller chain is snd_emu10kx β mpu401_init β
midi_init (emu10kx-midi.c:201, mpu401.c:185). The audit guest has no
EMU10Kx PCI device (QEMU has no model for it), so no MPU_INIT can ever
run, let alone fail, there. Faking it would require modifying the read-only
sys/ tree / out-of-tree module, out of audit scope. The defect is
certain from source; only its real-world firing is hardware-dependent.
Fix
fix.diff: release both locks before lockuninit on err2, and
kfree(m->synth, M_MIDI) on err1 (err2 falls through into err1, covering
both). No kernel build validation for the same hardware reason.
Bottom line
Genuine error-path defect pair (panic + leak) in midi_init, certain at source level, gated on midi hardware attach failure β Low severity, defense-in-depth value.
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: the failing MPU_INIT attach path cannot be staged on this guest (no emu10kx hardware). fix.diff is a minimal, self-evident error-path correction.
source-level: fix.diff vs midi.c:371-380
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md proof chain: midi.c:325-326 (locks acquired) vs midi.c:354-355,371-373 (err2 lockuninit with locks held) vs kern_lock.c:1325-1336 (KKASSERT); grep shows no kfree(m->synth) anywhere in sys/
PoC changes
no seed; no runnable PoC possible (hardware-gated error path)
Verified recommended fix
release both locks before lockuninit on err2 and kfree(m->synth) on err1 (fix.diff)
Verdict
midi_init() error paths are defective with certainty at source level: err2 (reached with m->lock and m->qlock held after MPU_INIT failure) calls lockuninit() on both locks, which KKASSERTs they are unheld (kern_lock.c:1330-1335) -> panic; and m->synth (allocated at midi.c:311) is never freed on err1/err2 nor in midi_destroy() -> leak. Not runtime-verifiable on this guest: the error path requires an MPU driver attach failure, and the only in-tree midi_init caller is snd_emu401/emu10kx hardware which QEMU cannot emulate.
No comments yet.