# DF-4157 — ADC DMA-ring feed trusts the APU CURPTR register as its only bound

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

## What

The record-channel completion walk derives its ring index exclusively from a
device register:

```c
cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR);     /* 16-bit hw register */
cur -= 0xffff & ((ch->srcphys - ch->base) >> 1);  /* unclamped */
...
interleave(ch->src + last, src2 + last, ch->sink + 2*last, cur - last);
```

(`maestro.c:1090-1104`, mono variant `maestro.c:1128-1139`). `cur` is
`unsigned`; if the register read returns a value below the base offset the
subtraction underflows toward 2^32 and `interleave()`/`mixdown()` then write
2 int16 per iteration — an unbounded OOB write past the 3*bufsz `stat`
busdma allocation into adjacent kernel memory.

In nominal operation the APU wraps at LOOPLEN so `cur ∈ [0, buflen/2)` and
everything is consistent — the software math is only as good as the
register. Desync windows that the driver itself creates:

1. **Power-state TOCTOU**: `agg_intr()` tests `ess->curpwr <= D1`
   (`maestro.c:1653,1675`) *without* `agg_lock`, while `agg_suspend()` /
   `agg_power(..., D3)` (`maestro.c:821-832, 1985-1995`) powers the part
   down under the lock. An interrupt that already passed the check feeds
   from a dead register file after `pci_set_powerstate(D3)`.
2. **stop/start race**: `aggch_stop_adc()` clears the `active` bit and sets
   the APUs INACTIVE under the lock (`maestro.c:1046-1053`) while
   `agg_intr()` may already be past its `ess->active & m` test and about to
   `chn_intr(ess->rch.channel)` → `PCMTRIG_EMLDMARD` → feed.

The author's own comment ("This function expects n not overwrapping the
buffer boundary", `maestro.c:1066-1070`) documents the trust.

## Impact ceiling

Kernel OOB write with attacker-influenced *contents* (the L/R sample data
being interleaved is the recorded audio) and huge length — a corruption
primitive, not just a panic, on any machine with a Maestro card when the
race hits. Hardware-gated (needs the card + suspend/resume or a
stop/rstorm race), hence Medium/likely rather than High.

## Guest result

Audit guest has no ESS Maestro PCI function and no /dev/dsp (run.log) —
the driver never attaches. `untested` on this guest; defect is
source-certain. `wp_rdapu` itself even prints "indexing timed out" when
the register won't echo (`maestro.c:455-456`) and *still* returns whatever
`PORT_DSP_DATA` holds — there is no error propagation either.

## Fix

`fix.diff`: clamp the register-derived index to the ring size in both feed
functions (treat desync as "wrapped"). Applies cleanly with `git apply`.
