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

feed_eq_feed FXROUND/FXDIV align-overshoot at the SND_FXDIV_MAX boundary: reads/writes up to align-1 bytes past the caller's byte window and returns count+align-1 (sndbuf_acquire ring overflow; tmpbuf OOB write when the 64KB-exact allocation geometry holds)

Summary

The generated FXDIV reciprocals misround UP at x=SND_FXDIV_MAX and a ~12-value window below it for every non-power-of-2 divisor, so FXROUND grows count past the caller's window by up to align-1 (4-14 bytes for multichannel S16/S8), pulls that many bytes from the source, biquad-reads AND writes all of them, and returns count+align-1 - which sndbuf_acquire copies into a ring with only count free, breaking ready/free invariants. When the dest tmpbuf is an exactly-65536-byte kmalloc (driver-default 64KB hard buffers), a 4-14 byte heap OOB write with biquad-filtered audio content. GUEST-SOAKED: 32.4M checks against the PRISTINE file show 106,400 exactly-'feed <= count (65548 vs 65536)' failures at 7ch S16 (align 14); patched copy 0. The identical FXROUND idiom lives in feeder_volume/format/matrix/mixer (default-config exposure without EQ) - noted for follow-up. Clamp-the-count fix (A/B-validated).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4710 Β· 22 files
FileTypeDescriptionSize
eqfuzz.c β€” 15.2 KB view raw
eqshim.h β€” 5.9 KB view raw
feeder_eq.c β€” 17.4 KB view raw
feeder_eq_fixed.c β€” 18.0 KB view raw
feeder_eq_gen.h β€” 21.8 KB view raw
snd_fxdiv_gen.h β€” 3.6 KB view raw
eqchurn.c β€” 3.0 KB view raw
ospace.c β€” 1.7 KB view raw
dbg4.c β€” 1.9 KB view raw
build.sh β€” 65 B view raw
run.sh β€” 89 B view raw
build.log β€” 1.1 KB view raw
run.log β€” 3.8 MB ↓ download
run.2.log β€” 182 B view raw
oob_8.log β€” 112 B view raw
oob_9.log β€” 130 B view raw
oob_11.log β€” 0 B ↓ download
oob_16.log β€” 0 B ↓ download
divergence.log β€” 68 B view raw
env.txt β€” 332 B view raw
fix_4710.diff β€” 983 B view raw
code_hashes.txt β€” 467 B view raw

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (eqfuzz baseline: checks=32437264 failures=106400, all 'feed <= count (65548 vs 65536)' at 7ch S16_LE align 14); run.2.log (eqfuzz_fixed: failures=0); build.log (gcc 8.3 warnings incl. the DF-4711 UB flag); VERDICT.md (full chain with path:line)

PoC changes

PoC authored fresh in pass 2 (no seed): userland harness compiling the pristine feeder_eq.c with faithful shims (eqshim.h) + the real build-time generated headers (feeder_eq_gen.h, snd_fxdiv_gen.h reproduced via the sys/conf/files awk invocations). Two harness bugs fixed during iteration (BYPASS passthrough assertion; preamp value mapping sign; count 65537 violated the kernel-side <=SND_FXDIV_MAX precondition and was removed).

Verified recommended fix

Clamp feed_eq_feed count into the SND_FXDIV-exact domain (see fix_4710.diff); systemically, fix the snd_fxdiv_gen.awk reciprocals or cap maxfeed strictly below SND_FXDIV_MAX in buffer.c:707.

Verdict

Reproduced at feeder level on the audit guest against the real, unmodified sys/dev/sound/pcm/feeder_eq.c: feed_eq_feed() FXROUNDs its byte count up past the caller's window at the SND_FXDIV_MAX boundary for non-power-of-2 sample alignments (SND_FXROUND(65536,14)=65548), biquad-reads and writes up to align-1 (4-14) bytes past that window, and returns count+align-1 to sndbuf_acquire (ring overflow with only count free). 32.4M-check harness run: baseline 106400 overshoot failures; patched copy 0. In-kernel heap write is likely but was not directly observable on this rig: every reachability conjunction is individually verified (buffer.c:707 cap admits the window, channel.c:430/561 deliver 65536 on a full drain of a 64KB hard buffer, buffer.c:226 tmpbuf=round_page(65536) exact allocation, non-pow2 aligns are ordinary 5.1/6.1 user formats), but the production guest kernel has no INVARIANTS and ES1370 defaults to a 4KB hard buffer (64KB needs hint.pcm.0.buffersize; set and rebooted - no crash, no GETOSPACE anomaly, as expected for a 12-byte write past a 64KB kmalloc with no detector). EQ itself requires the root-set hint.pcm.N.eq=1, capping this file's finding at Medium; the same FXROUND idiom in feeder_volume/format/matrix/mixers runs in default configs.