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)
PoC verification
Evidence pack
findings/poc/DF-4710 Β· 22 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
- sys/dev/sound/pcm/feeder_eq.c:464
- sys/dev/sound/pcm/feeder_eq.c:470
- sys/dev/sound/pcm/feeder_eq.c:173-212
- sys/dev/sound/pcm/buffer.c:707
- sys/dev/sound/pcm/buffer.c:615-636
- sys/dev/sound/pcm/buffer.c:226
- sys/dev/sound/pcm/channel.c:430
- sys/dev/sound/pcm/channel.c:561
- sys/dev/sound/pcm/channel.h:443
- sys/dev/sound/pci/es137x.c:117
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.
No comments yet.