β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1525

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:

  1. Open enough /dev/dspN instances to create virtual channels up to snd_maxautovchans per device per direction, filling the channel list.
  2. Open any /dev/dsp or /dev/mixer and issue ioctl(fd, SNDCTL_SYSINFO, &si).
  3. Observe that si.numsynths (or later fields) have unexpected bit patterns set β€” the |=(1<<n) corruption from the OOB write.
  4. With maxautovchans=256 and 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.

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1525 Β· 4 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix for the cited bug 440 B view raw
VERDICT.md verdict source-confirmation analysis 707 B ↓ raw
build.sh build-script N/A (source-only) 61 B view raw
run.sh run-script N/A (source-only) 87 B view raw
VERDICT.md verdict source-confirmation analysis
↓ download 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 verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff compiled in batch kernel build rc=0 -Werror

fix.diff compiled in batch kernel build rc=0 -Werror
↓ fix.diffcombined build rc=0

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)