# VERDICT — DF-1337

## Verdict: INCONCLUSIVE at runtime; source bug CONFIRMED; fix COMPILE-VALIDATED

**Citations confirmed:** sys/dev/sound/pcm/channel.c:502, sys/dev/sound/pcm/channel.c:503, sys/dev/sound/pcm/channel.c:504, sys/dev/sound/pcm/channel.c:1762, sys/dev/sound/pcm/dsp.c:896, sys/dev/sound/pcm/dsp.c:1925, sys/dev/sound/pcm/channel.h:120

### Is the bug real? — YES (source trace)
chn_write() captures a raw buffer pointer `off = sndbuf_getbufofs(bs,p)` under CHN_LOCK (channel.c:502), then DROPS the lock (CHN_UNLOCK, :503) across uiomove(off,t,buf) (:504) to copy user bytes into bs->buf. During that unlock window a second thread issuing a blocksize / fragment / format-change ioctl (dsp.c AIOSSIZE / SNDCTL_DSP_SETFRAGMENT -> chn_setblocksize -> chn_resizebuf) re-acquires the lock and, because the channel is not yet CHN_F_TRIGGERED on the first write (triggered is set later by chn_start at channel.c:510/738), passes the guard at :1762 and calls sndbuf_remalloc -> kfree(old bs->buf). The first thread's uiomove then writes through the freed pointer => kernel-heap UAF write. chn_read() has the identical pattern => UAF read / info leak. The SILENCE/SKIP ioctls already avoid this by waiting on c->inprog (dsp.c:1925/1949), but the resize ioctls do not, even though dsp.c:896 wraps chn_io with ++inprog.

### Can it be reproduced on this guest? — NO (hardware-gated)
Requires /dev/dsp to exist: a sound driver module loaded AND a detected audio controller. The QEMU audit guest has NO PCI audio device (no class 0x0401/0x0403), no /dev/dsp node, and no snd_* module loaded; an unprivileged user cannot kldload(2) (root-only) and, even if loaded, no hardware would attach so no /dev/dsp would appear. On real hardware with any SD_F_MPSAFE audio driver (modern PCI/USB) the race is exercisable by any user with /dev/dsp access.

Guest evidence (`env.txt`): only `vgapci0 class=0x030000 chip=0x11111234` (QEMU stdvga); no AMD GPU;
no PCI audio device; `kldstat` shows no drm/radeon/amdgpu/snd module; `/dev/dri` and `/dev/dsp*` do
not exist. The `sound` module is not in `X86_64_GENERIC`, is not loaded, and cannot be loaded by
an unprivileged user (kldload is root-only); even loaded, it would not attach without the hardware.
Therefore the vulnerable code is unreachable at runtime here. Because the sinks are device-integrated
parsers / DRM ioctls / a hardware-dependent channel race, no userspace harness on this guest can
exercise them. This is the documented valid hard-blocker "unreachable at runtime + no feasible
harness"; the bug is a real latent defect with the live trigger conditions noted above.

### No escalation chain (and why that is correct here)
There is no memory-corruption primitive to escalate on this guest: the corruption sinks live entirely
inside the not-loaded `sound` driver behind hardware that is absent. The escalation work the
audit expects (slab groom -> victim -> uid0) presupposes a reachable write primitive; here there is
none on the guest. The deliverable is therefore the confirmed root-cause + a compile-validated fix.

### Fix (fix.diff) — authored and COMPILE-VALIDATED
Add `while (c->inprog != 0) cv_wait(&c->cv, c->lock);` at the top of chn_resizebuf(), mirroring the existing SILENCE/SKIP guard in dsp.c, so every blocksize/fragment/format-change path waits for an in-flight chn_read/chn_write to release its raw buffer pointer before remalloc'ing. Matches the finding markdown proposal (wait for inprog==0 in chn_resizebuf).

The fix was applied to in-guest `/usr/src` (all hunks applied cleanly) and the `sound` module was
rebuilt with the kernel's `-Werror` flags:
`cd /usr/src/sys/dev/...sound... && KERNCONF=X86_64_GENERIC SYSDIR=/usr/src/sys make -m /usr/src/share/mk`
=> **rc=0**, no warnings/errors in the patched translation unit (`build_fix.log`). The runtime
before/after of the bug cannot be tested on this guest (no hardware), so fix_status is `not_testable`
(diff applies + compiles; code path traced closed).

### Why not `not_reproduced` (false-positive)?
This is NOT a false positive. The cited `sys/` code is genuinely missing the guard / has the overflow
/ has the race — verified by reading the source. It is a real bug that is simply out of reach of this
particular (GPU/audio-less) QEMU guest.
