# DF-4421 — snd_ich attach-failure path leaves live 0666 /dev/mixerN over freed AC97 codec + softc (UAF on open/ioctl)

6th instance of the pcm attach-error family (DF-4156 maestro, DF-4193 es137x,
DF-4299 emu10kx-pcm, DF-4334 via8233, DF-4344 atiixp), this time in
`sys/dev/sound/pci/ich.c`.

## TL;DR

`ich_pci_attach()` calls `mixer_init()` (ich.c:1031) — which publishes a
0666 `/dev/mixerN` whose `si_drv1` chain reaches the AC97 codec — *before*
five more failure paths that all jump to `bad:` (ich.c:1045, 1053, 1060,
1065, 1069). `bad:` frees the codec (`ac97_destroy`, ich.c:1096-1097) and the
softc (`kfree`, ich.c:1118) but never tears the mixer cdev down. Any later
`open`/`ioctl` on `/dev/mixerN` dispatches through the freed codec's
kobj'd methods table → use-after-free (read/write primitives through
`mixer_ioctl` → `mix_getdevinfo` → freed `struct ac97_info`).

Not runtime-reproducible on the audit guest: QEMU (i440FX machine) presents
no Intel ICH AC'97 controller (no `class=0x0401` device; only 8086 chips
1237/7000/7010/7113 — see `guest_check.log`). `snd_ich.ko` loads but
`ich_pci_probe()` never matches, so `ich_pci_attach()` never executes.
Source-certain by construction and by symmetry with the 4 runtime-verified
family members.

## How to reproduce (on hardware with an ICH AC'97 controller)

 Preconditions: any machine with an Intel ICH/ICH2/.../ICH7, SiS 7012,
 nForce, AMD-768/8111 AC'97 controller (PCI class 0401, e.g. 8086:2415),
 plus one of:

  * memory pressure sufficient to fail `bus_dmamem_alloc`/`bus_dmamap_load`
    (ich.c:1060/1065), or
  * `bus_dma_tag_create` failure (ich.c:1045/1053), or
  * 33+ sound devices so `pcm_register` hits `PCMMAXUNIT` → ENODEV
    (sound.c:1027-1033), or
  * a boot where feeder init left `pcm_veto_load=1` → `pcm_register`
    returns EINVAL (sound.c:1021-1024).

```sh
# with hw.snd.maxunit exhausted (33rd sound card) or any alloc failure:
kldload snd_ich            # attach fails AFTER mixer_init succeeded
ls -l /dev/mixer0          # device still exists, 0666 root:wheel
dd if=/dev/mixer0 bs=64 count=1 | hexdump -C   # UAF read path
# or any SNDCTL_MIXER_* ioctl via /dev/mixer0  → UAF through freed codec
```

On the audit guest this is impossible — no AC'97 PCI function exists, so
the trigger never reaches `ich_pci_attach()` (`guest_check.log`).

## Artifacts

  * `VERDICT.md`         full narrative + root-cause table + negative results
  * `guest_check.log`    proof of hardware gate (no 0401 PCI device, no pcm dev)
  * `run.sh`             exact guest check commands (what was run)
  * `trigger.hw.sh`      the real-hardware trigger script
  * `fix.diff`           family-consistent fix (pcm_register before mixer_init)
  * `env.txt`            guest environment
  * `manifest.json`      machine-readable catalog
  * `verdict.json`       machine verdict (consumed by audit/persist_poc.py)

## Fix

Reorder `pcm_register()` ahead of `mixer_init()` (no failure path may remain
after the mixer cdev is published) and check `mixer_init()`'s return — the
same shape accepted for DF-4334 (via8233). See `fix.diff`.
