# 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]:

1. **lockuninit on held locks → panic.** `m->lock` and `m->qlock` are
   acquired at midi.c:325-326 *before* the failure points that jump to
   `err2` (the `MIDIQ_BUF` sanity check at 343-345 and, realistically,
   `MPU_INIT(m, cookie)` failure at 354-355). `err2` [371-373] then calls
   `lockuninit(&m->qlock); lockuninit(&m->lock)` **while both locks are
   still held**. DragonFly's `lockuninit()` 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).

2. **m->synth leak.** `m->synth` is allocated at midi.c:311, but every
   destruction/error path frees only `m`: err2 → err1 `kfree(m, M_MIDI)`
   [380], and — beyond the init path — `midi_destroy()` [1432-1436] also
   never frees `m->synth`. Every failed init and every successful
   `midi_uninit`/`midi_destroy` leaks `sizeof(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.
