cs4281: unguarded divide in cs4281_rate_to_rv would panic if rate==0 reaches the driver
| Field | Value |
|---|---|
| ID | DF-1681 |
| File | sys/dev/sound/pci/cs4281.c |
| Lines | 209, 221, 368, 383 |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-369 Divide By Zero |
| Confidence | speculative |
| Status | new |
| CVE match | variant (divide-by-zero class β DF-1603/1611/1612/1616/1648/1650) |
| Created | 2026-07-18 |
Summary
cs4281_rate_to_rv() executes v = 1536000 / rate with no local
zero-check on the user-influenced rate. A zero sample rate would trap a
divide-by-zero in kernel mode and panic the system (local DoS).
The PCM core currently prevents this: chn_setparam (channel.c:1974)
rejects speed < 1 and the vchan path clamps bestspeed
(channel.c:2420-2421), and cs4281_pci_resume passes a stored nonzero
spd. So today the value reaching the driver is always >=
DSP_DEFAULT_SPEED (8000) / caps minspeed (6024).
The defect is latent: a kernel driver that trusts a framework invariant
to avoid a divide-by-zero is fragile, and any future caller (or a
vchan/feeder change) that bypasses chn_setparam would turn this into a
reachable local panic.
Root cause
cs4281_rate_to_rv (cs4281.c:209-221) loops the fixed rate table, and
on miss falls through to:
v = 1536000 / rate; /* cs4281.c:218 β no zero guard */
rate is the u_int32_t speed argument threaded in from
cs4281chan_setspeed (cs4281.c:369-383, called as the channel_setspeed
KOBJ method) and ultimately from CHANNEL_SETSPEED
(channel.c:1985,2431,2452).
cs4281_rv_to_rate (cs4281.c:223-231) has a symmetric latent issue:
r = 1536000 / rv at :229 would trap if rv == 0, but rv == 0 always
takes the table branch (rv < CS4281_NUM_RATES β cs4281_rates[0]), so
it is safe in practice.
cs4281_rate_to_rv is the only one with a live zero-divide code path.
Threat model
Local unprivileged user with access to the audio device (default devfs perms, or any user in the audio group) setting a sample rate of 0.
Impact: deterministic kernel divide-by-zero trap β system panic β local denial of service.
Currently NOT reachable because the PCM core's chn_setparam
(channel.c:1974 if (speed < 1 ...) return EINVAL;) and the vchan
bestspeed clamp (channel.c:2420-2421) guarantee speed >= 1 before
CHANNEL_SETSPEED dispatches here, and cs4281_pci_resume only passes
previously-stored nonzero speeds.
Reported as Info because the bug is real but the trigger is presently
framework-blocked; it becomes a real Medium-DoS the moment any path
bypasses chn_setparam.
PoC
Cannot be reproduced on a stock DragonFlyBSD kernel today because
chn_setparam (sys/dev/sound/pcm/channel.c:1974) rejects speed < 1
before the driver is reached.
A PoC that proves the latent defect by calling the function directly:
/* trigger.c - proof that cs4281_rate_to_rv traps on rate==0 */
#include <sound/pci/cs4281.h> /* for the function if exported, or link a kld */
/* In a kernel module: */
cs4281_rate_to_rv(0); /* divides 1536000/0 -> deterministic kernel trap/panic */
Build: put in a kld module, make in a module tree referencing the cs4281
source, kldload ./trigger.kld. Expected: immediate divide-by-zero trap β
kernel panic trap division by zero.
On stock userland the ioctl path SNDCTL_DSP_SPEED(fd,0) / AOSSPEED 0
is rejected at channel.c:1974 and never reaches the driver, so a
userspace PoC will NOT reproduce it β this is exactly why the finding is
Info/speculative rather than Medium.
Recommended fix
Add a local zero-guard in the driver so it does not depend on the framework invariant. Defense in depth; behavior unchanged for all valid rates.
--- a/sys/dev/sound/pci/cs4281.c
+++ b/sys/dev/sound/pci/cs4281.c
@@ -214,6 +214,9 @@ cs4281_rate_to_rv(u_int32_t rate)
for (v = 0; v < CS4281_NUM_RATES; v++) {
if (rate == cs4281_rates[v]) return v;
}
+
+ if (rate == 0)
+ return 5; /* avoid divide-by-zero; default to 8k */
v = 1536000 / rate;
if (v > 255 || v < 32) v = 5; /* default to 8k */
Related findings
Divide-by-zero class:
- DF-1603 (virtio_blk.c
blk_size=0) - DF-1611/1612 (dcn10_hubp.c pixel_clk/htotal)
- DF-1616 (ath_beacon.c
lintval==0) - DF-1648 (freesync.c
min_vfreq==0) - DF-1650 (rs780_dpm.c
drm_mode_vrefresh()==0)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1681 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-only confirmation + mechanism + fix | 1.5 KB | β raw |
| fix.diff | suggested-fix | Guard: if (rate == 0) return default (5). | 632 B | view raw |
| ../fix_build_new.log | build-log | Batch kernel build with new fixes (rc=0, -Werror) | 5.6 MB | β download |
DF-1681 β PoC Verification Verdict
Category: sound (IN GENERIC)
Source: sys/dev/sound/pci/cs4281.c:209-225
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR)
Date verified: 2026-07-25
Verdict: REPRODUCED (source-only confirmation; GENERIC-compiled, no HW)
Mechanism
cs4281_rate_to_rv(rate): the for-loop falls through with rate not in table; line 218 computes v = 1536000/rate. If rate==0 reaches the driver from a channel-set call, #DE panic.
In GENERIC kernel build: YES (file compiled by X86_64_GENERIC)
Reproduction status
This finding is GENERIC-compiled but trigger requires specific runtime state: the vulnerable code path requires specific runtime state (specific device probe, RAID config, sysctl, or process context) not reproducible from the unprivileged audit guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not exercised. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.
Fix
Guard: if (rate == 0) return default (5).
See fix.diff for the standalone git-apply-able unified diff. Validated by applying the 38 new-finding batch diffs (including this one) and building a single X86_64_GENERIC kernel (rc=0, -Werror clean).
Fix verification
fixedVALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
REPRODUCED (source-only): cs4281_rate_to_rv(rate): for-loop falls through with rate not in table; line 218 computes v=1536000/rate. If rate==0 reaches driver, #DE panic.
Verified recommended fix
REPRODUCED (source-only): cs4281_rate_to_rv(rate): for-loop falls through with rate not in table; line 218 computes v=1536000/rate. If rate==0 reaches driver, #DE panic.
Verdict
REPRODUCED (source-only): cs4281_rate_to_rv(rate): for-loop falls through with rate not in table; line 218 computes v=1536000/rate. If rate==0 reaches driver, #DE panic.
No comments yet.