# DF-4156 — agg_attach() error path frees the AC97 codec while the 0666 /dev/mixerN it created survives

File: `sys/dev/sound/pci/maestro.c` (pass 2, GLM 5.3)

## What

`agg_attach()` calls `mixer_init()` (which `make_dev`s **/dev/mixerN with mode
0666**, `mixer.c:730`) *before* `pcm_register()` (`maestro.c:1894-1903`).
If `pcm_register()` subsequently fails, the `bad:` label runs
`ac97_destroy(codec)` (`maestro.c:1923-1924`) **without** first tearing the
mixer down. `/dev/mixerN` keeps existing with `pdev->si_drv1 = m` and
`m->devinfo = codec`, both dangling after the free.

Any unprivileged user that opens the surviving `/dev/mixerN` and issues a
mixer ioctl walks:

```
mixer_ioctl (mixer.c:1196)
  -> mixer_ioctl_channel -> mix_* -> mixer_set (mixer.c)
     -> MIXER_SET(m, ...) -> ac97_set -> ac97_wrcd (ac97.c:330)
        -> AC97_WRITE(codec->methods, codec->devinfo, ...)   [both freed]
```

`ac97_destroy()` (`ac97.c:852`) does `kobj_delete(codec->methods)` and
`kfree(codec)` — so the ioctl path dereferences freed heap and dispatches
through a freed kobj: use-after-free read + indirect-call-through-freed-
memory. The correct unwind order is demonstrated by `pcm_unregister()`,
which calls `mixer_uninit(dev)` first (`sound.c:1205`).

## Precondition (why this is Low)

`pcm_register()` only fails early (`sound.c:1021-1027`): `pcm_veto_load`
stuck at 1 (root-feeder SYSINIT never ran — a boot-time anomaly), or
`device_get_unit(dev) > PCMMAXUNIT` (255). Not attacker-creatable without
prior root. With the precondition met, *use* of the UAF needs no privilege
(0666 node).

Related symptom of the same asymmetry: `agg_detach()` never calls
`ac97_destroy(ess->codec)` at all — the codec leaks on every detach
(module unload). The fix addresses both.

## Reproduce

```
build.sh   # cc maestro_probe.c
run.sh     # probes for the pcm/mixer precondition
```

Requires a machine with an ESS Maestro PCI audio function **and** a kernel
where `pcm_register()` fails after `mixer_init()` succeeded. The audit guest
has no Maestro hardware and no pcm devices (see run.log), so the trigger
cannot be instantiated there; the defect itself is source-level certain.

## Fix

`fix.diff`: call `mixer_uninit(dev)` before `ac97_destroy(codec)` when the
mixer was initialized, and destroy the codec in `agg_detach()` (fixes the
leak). Applies cleanly with `git apply`.
