Unbounded channel index causes OOB write past openedaudio[] in sound_oss_sysinfo
- File:
sys/dev/sound/pcm/sound.c - Lines: 1308, 1325, 1329, 1330, 1332
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U:C:N/I:L/A:L - CWE: CWE-787 Out-of-bounds Write
- Confidence: likely
Summary
sound_oss_sysinfo() iterates over every channel of every registered PCM device
incrementing a counter j with no upper-bound check, then writes
si->openedaudio[j / 32] |= (1 << (j % 32)).
The openedaudio array has only 8 elements (256 bits).
When total system-wide PCM channels reach 256, the write escapes the array and
corrupts adjacent oss_sysinfo fields; with extreme configurations
(j > 8384) it overflows the heap-allocated ioctl buffer entirely.
Root cause
At sound.c:1308, j is initialized to 0.
The loop at sound.c:1310-1336 iterates every PCM device and every channel via
CHN_FOREACH, incrementing j unconditionally at line 1332 (j++) for each
channel regardless of whether it is busy.
The write at lines 1329β1330,
si->openedaudio[j / intnbits] |= (1 << (j % intnbits)), uses j as a bit
index into openedaudio[8] (soundcard.h:1748, 8 ints = 256 bits).
There is no check that j < 256 before indexing.
When j >= 256, the write lands on numsynths (byte 232 of the struct), then
nummidis, nummixers, openedmidi, and onward.
The oss_sysinfo struct is 1248 bytes; openedaudio starts at byte 200. Once
j/32 exceeds 262 (j > 8384), the write escapes the struct and corrupts the
adjacent kmalloc(M_IOCTLOPS) heap object.
The ioctl buffer is heap-allocated because sizeof(oss_sysinfo)=1248 >
STK_PARAMS=128 (sys_generic.c:572,674).
Threat
An unprivileged local user can reach this via SNDCTL_SYSINFO on /dev/dsp
(mode 0666, sound.c:1136) or /dev/mixer (mode 0666).
On default config (snd_maxautovchans=16), each PCM device yields up to ~34
channels (2 hw + 16 play vchan + 16 rec vchan), so 8+ sound devices with
sufficient open dsp sessions push j past 256.
The immediate impact is corruption of the SNDCTL_SYSINFO output struct within
its own buffer (logic bug).
With root-set snd_maxautovchans=256 and 17+ PCM devices
(PCMMAXUNIT=255), j can exceed 8384 and overflow into adjacent kernel heap,
potentially enabling code execution or panic.
The write is a bitwise OR (|=), so the attacker controls which bit is set but
cannot write arbitrary full values.
Exploit / PoC
Prerequisites: a system with 8+ registered PCM sound devices (e.g. multiple USB
audio dongles), or root-configured hw.snd.maxautovchans=256.
PoC sketch:
- Open enough
/dev/dspNinstances to create virtual channels up tosnd_maxautovchansper device per direction, filling the channel list. - Open any
/dev/dspor/dev/mixerand issueioctl(fd, SNDCTL_SYSINFO, &si). - Observe that
si.numsynths(or later fields) have unexpected bit patterns set β the|=(1<<n)corruption from the OOB write. - With
maxautovchans=256and enough devices, the kernel may panic from heap corruption.
Build: cc -o poc_sysinfo poc_sysinfo.c.
The SNDCTL_SYSINFO ioctl is _IOR('X',1,oss_sysinfo) per soundcard.h:1978.
Success = si.numsynths != 0 when no synths exist, or kernel panic in extreme
configs.
Recommended fix
Add a bounds check on j before writing to openedaudio. Clamp j at 256
(8 * 32 bits) which is the capacity of the openedaudio bitmap:
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -1325,8 +1325,11 @@ sound_oss_sysinfo(oss_sysinfo *si)
CHN_FOREACH(c, d, channels.pcm) {
CHN_UNLOCKASSERT(c);
CHN_LOCK(c);
- if (c->flags & CHN_F_BUSY)
+ if (c->flags & CHN_F_BUSY &&
+ j < nitems(si->openedaudio) * intnbits)
si->openedaudio[j / intnbits] |=
(1 << (j % intnbits));
CHN_UNLOCK(c);
j++;
}
Alternatively, define
#define OSS_MAX_AUDIOENGINES \
(nitems(((oss_sysinfo *)0)->openedaudio) * (sizeof(int) * 8))
and break the j counter when it reaches that limit.
The fix ensures the bitmap write never escapes the openedaudio array regardless
of how many channels exist system-wide.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1525 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited bug | 440 B | view raw |
| VERDICT.md | verdict | source-confirmation analysis | 1.0 KB | β raw |
| build.sh | build-script | N/A (source-only) | 65 B | view raw |
| run.sh | run-script | N/A (source-only) | 89 B | view raw |
DF-1525 VERDICT
Verdict: REPRODUCED (source-confirmed)
Impact: Low (driver-level NULL deref / OOB / leak / DoS β hardware-gated)
Mechanism: sound.c:1308 j=0 init; 1310-1336 CHN_FOREACH every PCM device every channel increments j unconditionally at 1332. Write 1329-1330 si->openedaudio[j/intnbits] |= (1 << (j%intnbits)). openedaudio[8] = 2
Citation: sys/dev/sound/pcm/sound.c:1308-1332
Fix: Applied fix.diff β compiles in batch kernel build (rc=0, -Werror).
Verification method: Source-only line-by-line trace of cited path:line. Low-severity driver bug; PoC trigger requires specific hardware or root context. Confirmed the cited vulnerable pattern exists in source.
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
fixedfix.diff compiled in batch kernel build rc=0 -Werror
fix.diff compiled in batch kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none (Low severity)
Evidence (decisive lines)
Source-confirmed: unbounded j indexes openedaudio[] OOB in sound_oss_sysinfo (sound.c:1329)
Verified recommended fix
Source-confirmed: unbounded j indexes openedaudio[] OOB in sound_oss_sysinfo (sound.c:1329)
Verdict
Source-confirmed: unbounded j indexes openedaudio[] OOB in sound_oss_sysinfo (sound.c:1329)
No comments yet.