Unconditional per-ioctl failure diagnostics + full chain rebuild per SETFMT/SPEED/CHANNELS give an unprivileged user an unbounded kernel console print rate; a one-shot guest-wide starvation/livelock was observed during such storms (inconclusive)
Summary
14 unconditional device_printf on failed rebuilds plus destroy-rebuild per format ioctl = unbounded kernel console print rate from an unpriv /dev/dsp holder. During pass-2 stress the 6-vCPU guest suffered a minutes-long whole-system starvation (sshd timeouts, continuous console spam) - NOT reproduced in two controlled re-runs, root-cause stack unobtainable (DDB reads input from the file-backed serial), and the QEMU rig lacks host audio (possible confounder). Filed speculative/inconclusive per honesty rules. ~1300 adversarial ioctls otherwise all held (no memory-safety defect in the chain builder). Gate-diagnostics-behind-snd_verbose hardening fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4602 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| feeder_fuzz.c | β | 4.1 KB | view raw | |
| wedge-serial-full.log | β | 24.0 KB | view raw | |
| rerun1.log | β | 237 B | view raw | |
| rerun2.log | β | 954 B | view raw | |
| README.md | β | 4.1 KB | β raw | |
| VERDICT.md | β | 3.4 KB | β raw | |
| build.sh | β | 119 B | view raw | |
| run.sh | β | 176 B | view raw | |
| env.txt | β | 608 B | view raw | |
| fix.diff | β | 4.0 KB | view raw | |
| verdict.json | β | 3.9 KB | view raw | |
| manifest.json | β | 1.2 KB | view raw |
DF-4602 β guest-wide starvation/livelock during unprivileged dsp format-change storms (INCONCLUSIVE)
File under audit: sys/dev/sound/pcm/feeder_chain.c
Observed event class: local DoS (kernel console spam + pcm lock churn starved a 6-vCPU guest for minutes)
Verdict: inconclusive β observed ONCE, NOT reproduced in 2 controlled re-runs; root-cause stack unobtainable on this rig; QEMU-without-audio-backend is a plausible confounder.
The event (original session, 2026-09-22 ~02:00-02:12, guest booted via vm_snd.sh)
Sequence: stage 1 = 3 unprivileged fuzzers on /dev/dsp0,/dev/dsp1 (30 s, no
wedge); stage 2 = 5 unprivileged fuzzers (incl. vchan clones /dev/dsp0.N
after dev.pcm.0.play.vchans=4) + concurrent root churn of
dev.pcm.0.play.vchanrate / vchanformat / bitperfect (no wedge during);
stage 3 = 3 O_RDWR fuzzers incl. the vchan node /dev/dsp0.0 (no immediate
wedge). One to three minutes AFTER all activity, the guest entered a
minutes-long starvation state:
- sshd unusable ("Connection timed out during banner exchange") for minutes at a time, intermittently recovering;
- qemu host process pegged at 94-107% (β1+ cores) with oscillating guest load;
- serial console (
wedge-serial-full.log) shows continuingpcm0/pcm1: feeder_build_format(): can't add feeder_format,feeder_build_rate(): can't set source rate,feed_format_init(): failed ...bursts β i.e. leftover fuzzer processes kept cycling failing feeder-chain rebuilds against pcm channels whose interrupt source is dead (QEMU audio backend absent); - no panic, no console output beyond the spam (no watchdog, no deadlock detector fired);
- the guest eventually self-recovered partially, then degraded again;
a Ctrl-Alt-Esc DDB break from the QEMU monitor (
Debugger("manual escape to debugger")in the serial log) stopped all CPUs, but DDB reads input from the serial console which is file-backed on this rig β nops/btawas obtainable β so the spinner's kernel stack was never captured.
Re-runs (controlled, after vm.sh reset with-src, fresh boot, vm_snd.sh)
- rerun1 (minimal): single unprivileged fuzzer,
/dev/dsp1, 45 s β NO wedge; cpu 10-20%, ssh ok at every 10 s poll (rerun1.log). - rerun2 (full original recipe): 5 fuzzers + identical root churn β
cpu 96-111% for ~2.5 min as expected, ssh ok except ONE transient
banner-exchange timeout at t+155 s, then healthy for the remaining ~4 min;
load 0.00 at the end; fuzzers exited cleanly (
rerun2.log).
Why filed at all
The one-shot event was severe (whole-guest starvation from unprivileged dsp
activity + root parent-format churn) and the in-file surface is real:
feeder_chain.c emits unconditional device_printf() diagnostics on
every failed rebuild (feeder_build_format :153/:163, feeder_build_rate
:226/:236, feeder_build_matrix :304/:314, feeder_build_volume :359/:370,
feeder_build_eq :427/:437, feeder_build_root :470/:477,
feeder_build_mixer :508/:518, plus feeder_chain :615/:626/:638/:645/:660/
:670), and every SNDCTL_DSP_SETFMT/SPEED/CHANNELS on a failing format
destroys and rebuilds the whole chain (:575-579). An unprivileged user can
sustain an unbounded rate of kernel console prints through this path
(console-spam DoS surface, CWE-400). Whether that surface alone explains the
guest-wide starvation β or whether the driver/dead-interrupt environment was
the real driver β could not be established.
Reproduce
# host$ dfbsd-qemu/vm.sh down && dfbsd-qemu/vm_snd.sh # host$ dfbsd-qemu/vm.sh run_root 'kldload snd_es137x; kldload snd_hda' build.sh # guest, as maxx: cc -O2 -o /tmp/feeder_fuzz /tmp/feeder_fuzz.c run.sh # guest, as maxx: the stage-2 recipe (see file) # watch: host qemu CPU%, ssh health, boot-snd.log spam
Recommended hardening
fix.diff β gate the high-frequency per-ioctl failure diagnostics behind
snd_verbose > 0 (keeps first-occurrence visibility via the existing
bootverbose/snd_verbose conventions). Clearly labeled hardening: it
removes the unbounded console-spam surface but is NOT claimed to fix the
one-shot livelock (root cause undetermined).
DF-4602 VERDICT β inconclusive
What happened
During the feeder_chain.c pass-2 stress campaign (see README.md for the exact
recipe), the guest entered a minutes-long starvation state ~1-3 minutes after
the fuzz activity: sshd refused banner exchanges for minutes at a time
(intermittently recovering), the host qemu process burned 94-107% of a core
with oscillating guest load (0.96 -> 1.83 -> decay -> repeat), and the serial
console kept emitting pcm0/pcm1: feeder_build_format(): can't add
feeder_format / feeder_build_rate(): can't set source rate /
feed_format_init(): failed ... bursts as leftover fuzzer processes cycled
failing feeder-chain rebuilds. No panic, no watchdog, no deadlock detector
fired. The guest never fully died (TCP stack alive throughout) but was
administratively unusable for minutes.
What I could not establish
- Root cause stack. A QEMU-monitor
sendkey ctrl-alt-escbroke into DDB (Debugger("manual escape to debugger"), all CPUs stopped β visible inwedge-serial-full.log), but DragonFly's console iscomconsoleand the rig's serial is file-backed, so DDB never saw my PS/2-injected keystrokes. Nops/bta-> spinner stack unknown. Candidates: console-lock contention from the print storm, CHN_LOCK/PCM_BUSY churn, or a driver-level softint livelock. - Determinism. After
vm.sh reset with-src: * rerun1 (minimal: 1 fuzzer, /dev/dsp1, 45 s): NO wedge, cpu 10-20%. * rerun2 (full original recipe: 5 fuzzers + identical root churn): cpu 96-111% for ~2.5 min as expected, ONE transient ssh banner timeout, then healthy for ~4 more minutes; load 0.00 at the end. - Real-hardware relevance. The QEMU rig has NO host audio backend (ALSA init fails on the host), so the guest audio devices never raise transfer interrupts β exactly the dead-interrupt environment in which chn_sleep-timeout / CHN_F_DEAD / rebuild-storm interactions concentrate. This confounder cannot be excluded and plausibly does not exist on real hardware.
Why filed (and at what confidence)
The in-file, indisputable component: feeder_chain.c emits unconditional
device_printf() diagnostics on every failed rebuild and every format
ioctl rebuilds the whole chain, so an unprivileged local user can sustain
an unbounded kernel console print rate through SNDCTL_DSP_SETFMT /
SPEED / CHANNELS storms (CWE-400 console-spam surface, lines :153, :164,
:227, :237, :305, :315, :361, :371, :429, :439, :472, :479, :509, :519 and
the destroy-rebuild loop at :575-579). Whether that surface alone explains
the observed whole-guest starvation is undetermined. Filed Low/speculative,
status inconclusive, impact dos (observed once).
Fix validation
Not possible: no reproducible baseline. fix.diff gates all 14 diagnostics
behind snd_verbose > 0 (compile-safe; snd_verbose is extern in
sound.h:314); it removes the spam surface and is the only defensible in-file
hardening, but is NOT claimed to fix the livelock.
Guest state
Returned clean: vm.sh reset with-src after evidence capture.
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.
Fix verification
not_testableNo reproducible baseline exists (one-shot event), so a single-fix kernel validation cannot be performed; the diff is a compile-safe hardening generated from the pristine file (applies by construction).
fix.diff (182-line unified diff gating all 14 unconditional device_printf diagnostics in feeder_build_format/rate/matrix/volume/eq/root/mixer behind snd_verbose > 0; snd_verbose is extern in sound.h:314)
Confirmed kernel references
Detail
Evidence (decisive lines)
wedge-serial-full.log (original session serial console: continuous pcm0/pcm1 feeder_build_* failure spam up to the 'Debugger(manual escape to debugger)' DDB break); rerun1.log (minimal trigger, no wedge); rerun2.log (full recipe, no wedge, load 0.00 at end); env.txt (QEMU has no host audio backend - ALSA init fails on host, so guest audio interrupts never fire)
PoC changes
n/a (fuzzer authored fresh for this batch; no seed PoC existed)
Verified recommended fix
Gate feeder_chain.c's per-ioctl failure diagnostics behind snd_verbose (fix.diff); if the livelock recurs on real hardware, capture ps/bta via a serial-input console or NMI panic and re-file with a determined root cause.
Verdict
A severe guest-wide starvation event (sshd banner-exchange timeouts for minutes, 94-107% qemu CPU with oscillating load, continuing feeder_build_format/feeder_build_rate console spam) occurred ONCE, 1-3 minutes after unprivileged dsp format-change fuzzing (incl. vchan clones) combined with root churn of dev.pcm.0.play.{vchanrate,vchanformat}/bitperfect on a QEMU guest whose audio devices have no host backend (interrupts never fire). Two controlled re-runs after vm.sh reset with-src (minimal single-fuzzer; full original recipe) did NOT reproduce it - only one transient ssh timeout in ~7 minutes of full-recipe load. Root-cause stack could not be captured: DDB accepts input only on the serial console, which is file-backed on this rig (a Ctrl-Alt-Esc monitor break stopped all CPUs but no ps/bta was possible). Filed because (a) the event was real and whole-guest in scope, and (b) the in-file surface is indisputable: feeder_chain.c emits unconditional device_printf diagnostics on every failed rebuild (:153,:164,:227,:237,:305,:315,:361,:371,:429,:439,:472,:479,:509,:519) and every SETFMT/SPEED/CHANNELS ioctl destroys and rebuilds the chain (:575-579), so an unprivileged user can sustain an unbounded kernel console print rate (CWE-400 console-spam surface). Whether that surface alone (vs the dead-interrupt QEMU audio environment or driver-level lock churn) explains the starvation is undetermined.
No comments yet.