# DF-4725 — aureal: attach `bad:` unwind frees `au` while the IRQ handler is still live (UAF write in au_intr) and releases NULL `reg[]` entries

**File:** `sys/dev/sound/pci/aureal.c` · **Severity:** Low · **Bucket:** memcorrupt · **Confidence:** certain

## Root cause (path:line)

The unwind block is ordered and guarded wrongly:

```c
 bad:                                             /* aureal.c:658 */
	if (au) free(au, M_DEVBUF);               /* 659 — FIRST! */
	for (i = 0; i < j; i++)
		bus_release_resource(dev, type[i], regid[i], reg[i]);  /* 660-661 */
	if (ih) bus_teardown_intr(dev, irq, ih);  /* 662 — AFTER free */
	if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
	return ENXIO;
```

### (a) live IRQ handler over freed softc

`snd_setup_intr(dev, irq, 0, au_intr, au, &ih)` at `aureal.c:620` installs
`au_intr(void *p)` with `p = au`. Interrupts are already enabled at that point:
`au_testirq()` (`aureal.c:528-537`, called at `aureal.c:625`) writes
`AU_REG_IRQEN = 0x00001030`, and `au_init()` (`aureal.c:505-506`) sets
`AU_REG_IRQGLOB |= AU_IRQ_ENABLE`. Every later failure path
(`au_init` fail `:627`, `AC97_CREATE` fail `:633`, `mixer_init` fail `:634`,
`bus_dma_tag_create` fail `:644`, `pcm_register` fail `:651`) runs `bad:`,
which **frees `au` before `bus_teardown_intr`**. In the window between the
free and the teardown, a level-triggered/shared PCI interrupt dispatches
`au_intr(au)` → `au->interrupts++` (`aureal.c:401`) is a **write into freed
heap**, and `au_rd(au, ...)` (`aureal.c:402`) reads bus-space tags/handles
(`au->st[]/au->sh[]`) from freed, reallocatable memory — UAF write/read in
hard-interrupt context. Correct order: teardown intr → release irq → release
mems → free `au` last.

### (b) NULL `reg[i]` handed to bus_release_resource

The BAR loop (`aureal.c:571-605`) increments `j` whenever *any earlier* BAR
mapped, even when the current BAR failed both allocations:

```c
	if (reg[j]) { au->st[i] = ...; mapped++; }   /* 591-595 */
	if (mapped) j++;                              /* 599 — runs even if reg[j]==NULL */
```

So BAR k failing after BAR 0..k-1 succeeded leaves `reg[j] == NULL` inside the
0..j-1 range that `bad:` unconditionally releases at `aureal.c:661`
(`bus_release_resource(dev, type[i], regid[i], NULL)` → NULL-deref panic in
rman).

## Impact

Both require the AU8820 card and an attach failure, hence Low: kernel UAF
write/read in interrupt context (a) or immediate NULL-deref panic (b).
(driver additionally not wired into any default build — see DF-4724 notes.)

## Verification

Hardware-gated (no Aureal PCI emulation in QEMU) — source-trace only.
Ordering semantics cross-checked against `sys/kern/subr_bus.c`
(`bus_teardown_intr` must precede the resource free that unmaps the handler's
target) and the DF-1506 (emu10kx) precedent for dangling-IRQ-on-attach-failure.

## Recommended fix

Rewrite `bad:` in the canonical order (teardown intr → release irq → release
regs, skipping NULL → destroy mixer/codec if live → free `au` last). The
`if (mapped) j++` must become `if (reg[j]) j++;` (or only track non-NULL
entries). See fix.diff.

## 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.
