# DF-4617 VERDICT — unvalidated stream tag in hdacc_stream_intr / hdacc_stream_free

STATUS: **not_reproduced** — live trigger hardware-gated (requires an HDA
controller with BSS > 0 whose DF-4115 tag arithmetic yields negative tags;
this guest's controller reports BSS = 0).
reproduced: 0 · impact: none on this guest · confidence in the code
mechanics: certain (source-traced, line by line, below).

## The defect (source truth)

`struct hdacc_softc` (hdacc.c:53-61):

    device_t        streams[2][16];   /* valid tag index: 1..15 */
    device_t        tags[64];         /* valid unsol tag: 0..63 */

### Sink 1 — hdacc_stream_intr (hdacc.c:702-710), the reachable one

    708:  if ((child = codec->streams[dir][stream]) != NULL)
    709:      HDAC_STREAM_INTR(child, dir, stream);

Caller: `hdac_one_intr()` (hdac.c:386-396) loops over all `num_ss` stream
slots and, for any set INTSTS bit with `sc->streams[i].dev != NULL`, calls

    HDAC_STREAM_INTR(dev, sc->streams[i].dir, sc->streams[i].stream);

`sc->streams[i].stream` is set in `hdac_stream_alloc()` (hdac.c:1932-1942)
from **controller-GCAP-derived arithmetic**. The BSS branch (hdac.c:1934)
carries the known DF-4115 sign error:

    stream = 15 - (ss - sc->num_iss + sc->num_oss);   /* = 15 - 2*num_oss - j */

which is negative whenever `2*num_oss + j > 15` (j = BSS index; e.g. any
controller with OSS ≥ 8, or OSS=4 with a 9th+ BSS stream). hdac stores the
negative tag **and** `sc->streams[ss].dev = child` unconditionally
(hdac.c:1940-1942). hdaa_channel_start rejects the returned `sid <= 0`
(hdaa.c:2170-2171), but the poisoned slot remains dev != NULL, so any
stream-interrupt bit for that index dispatches into hdacc_stream_intr with a
negative `stream`:

* stream = -1 → reads the softc `cad`+padding word (0) → NULL child →
  silently skipped (masked);
* stream = -2 → reads `codec->lock` (non-NULL!) → `HDAC_STREAM_INTR` method
  dispatch on a `struct lock *` — wild indirect call, certain crash;
* more negative (num_oss=15 → down to 15-30-30 = -45) → reads far outside
  the softc → semi-controlled garbage `device_t` → wild call.

No bounds check exists at the sink — hdacc.c:708 is the missing clamp.

### Sink 2 — hdacc_stream_free (hdacc.c:663-670)

    668:  codec->streams[dir][stream] = NULL;

Caller passes `ch->sid` (hdaa.c:2156-2157), which hdaa only ever sets to an
alloc return it validated `> 0` — so the NULL-write at a negative index is
*currently* unreachable through hdaa; the clamp is defense-in-depth.

## Why not reproduced on this guest

* Guest controller: QEMU Intel ICH6 (8086:2668), `hdac0@pci0:0:3:0`.
* Runtime caps (boot -v, captured on this same guest image by DF-4115,
  `findings/poc/DF-4115/caps.txt`): **`hdac0: Caps: OSS 4, ISS 4, BSS 0`**.
* `num_ss = 8`, `ss ∈ 0..7`; the BSS branch requires
  `ss >= num_iss + num_oss = 8` → structurally unreachable; every computed
  tag is in 1..4. No userspace action can make QEMU's ICH6 report BSS > 0,
  and no syscall path feeds the tag from user memory.
* Live exercise (this run, `dmesg.txt`): `hdac0 → hdacc0 → hdaa0 → pcm0`
  attach; 320 KB PCM → `/dev/dsp` (RC=0) exercising stream_alloc /
  streams[dir][1..4] / stream_intr dispatch — no panic, no unsol-tag spam,
  guest healthy.

This mirrors DF-4115's own verdict on the same hardware gate.

## Exploit chain (on affected hardware — hostile/faulty controller)

1. Controller reports GCAP with BSS > 0 and 2*num_oss + j > 15 for some BSS
   index j (e.g. OSS=8/BSS>0, or OSS=4/BSS≥9).
2. Audio open exhausts ISS/OSS or requests dir fallback → hdac_stream_alloc
   picks BSS slot → negative tag stored with dev != NULL (hdac.c:1940-42).
3. Controller asserts the stream's INTSTS bit (hostile device trivially
   can; a buggy one may via tag-collision chaos).
4. hdac_one_intr → hdacc_stream_intr → OOB read at streams[dir][negative]
   → wild `HDAC_STREAM_INTR` indirect call → kernel crash / unreliable
   control-flow hijack (call target is the OOB-read pointer, not directly
   attacker-chosen; a malicious controller already has DMA, so this is
   fault-injection amplification, not a new privilege boundary).

Ceiling: kernel memory-corruption-class primitive gated on non-default
audio controller hardware → **Low** severity, certain mechanics.

## Fix

`fix.diff` (git-apply-able, checked): clamp dir/stream at both sinks.
The true root cause is DF-4115's hdac.c arithmetic — fix upstream there;
the hdacc clamps make the sink safe against any future feeder bug.

fix_status: not_testable — the trigger is hardware-gated off on this guest
(BSS=0), so baseline-vs-patched behavior cannot be observed on the standard
snapshot; the clamp compiles and the happy path (playback) is unaffected.

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