feed_format_feed sizes its byte window with the inexact generated SND_FXROUND/SND_FXDIV table: misround-up within the claimed [1,65536] domain writes up to oalign-1 bytes past the destination window (or before it via the ialign path) and over-reports the return into sndbuf_acquire (ring overflow)
Summary
The awk generator validates its magic reciprocal at a single point, so the generated table misrounds UP for 15-40% of count values inside its claimed domain (first bad 4694/5473/8195 for align 15/14/12). feed_format consumes it on the caller's window: count rounded past the window (source writes past it; return>count -> ring overflow), the ialign path lands the source before the window, partial-return frames miscount. HARNESS-VERIFIED on the REAL pristine file with the REAL generated header: 8315/28200 canary checks fail; fixed generator: 0. Unpriv default-config via SETFMT on any encoding mismatch. One root cause, one generator fix (DF-4846..4849 family).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4846 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| VERDICT.md | β | 4.8 KB | β raw | |
| verdict.json | β | 5.1 KB | view raw | |
| fxshim.h | β | 6.9 KB | view raw | |
| fxsrc.c | β | 2.0 KB | view raw | |
| fxfmt.c | β | 6.6 KB | view raw | |
| fxfmt.c | β | 6.6 KB | view raw | |
| ffmat.c | β | 5.3 KB | view raw | |
| ffvol.c | β | 5.5 KB | view raw | |
| ffmix.c | β | 9.2 KB | view raw | |
| dspchurn.c | β | 2.1 KB | view raw | |
| ctlchurn.c | β | 991 B | view raw | |
| build.sh | β | 119 B | view raw | |
| run.sh | β | 140 B | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| snd_fxdiv_gen_fixed.awk | β | 4.0 KB | β download |
DF-4846 β feeder_format.c FXROUND align-overshoot (evidence pack)
What this proves
sys/dev/sound/pcm/feeder_format.c sizes its feed window with the build-time
generated fast-division macros (SND_FXROUND/SND_FXDIV from
sys/tools/sound/snd_fxdiv_gen.awk). The generated table is inexact within
its own claimed domain [1, 65536] for every non-power-of-2 divisor: it
misrounds UP (FXDIV = floor+1) for 15β40% of count values, starting as low as
xβ4694 (align 15), 5473 (align 14), 8195 (align 12). Consequences, each proven
by this harness on the REAL, UNMODIFIED file with the REAL generated header:
- T1
feeder_format.c:220count = SND_FXROUND(count, info->oalign)can EXCEED the caller's count β the whole pass is sized past the destination window β the source feeder writes past the window (REAR guard hit) and the return value exceeds count (in-kernel:sndbuf_acquire()ring overflow, buffer.c:615-636, only KASSERT-protected). - T2
feeder_format.c:233(ialign>oalign)j = SND_FXROUND(count, ialign)can exceed count βsrc = dst + count - j < dst(write BEFORE the window) and the source window exceeds the destination. - T3
feeder_format.c:240SND_FXDIV(ret, ialign)on a partial source return miscounts a frame up β the feeder converts and reports more output bytes than the source delivered (return inflation into sndbuf_acquire).
Build / run (guest)
cd /root/fx && sh build.sh # regenerates snd_fxdiv_gen.h via the real awk,
# copies real headers + the 4 feeder files, builds
./fxfmt # baseline: FAIL (8315/28200 checks)
cp snd_fxdiv_gen_fixed.h snd_fxdiv_gen.h && cc -O1 -g -o fxfmt_fixed fxfmt.c fxsrc.c
./fxfmt_fixed # fixed table: ALL PASS
Expected output
baseline fmt.log: fxfmt: checks=28200 failures=8315 -> FAIL with
FAIL[T1 ...] REAR guard (write past window) / return > count lines.
fixed fmt_fixed.log: failures=0 -> ALL PASS.
In-kernel reachability (traced, path:line)
- Chain insert:
feeder_chain.c:139-173(feeder_build_format) on any encoding mismatch β ordinary unprivilegedSNDCTL_DSP_SETFMTchoice. - Counts:
buffer.c:705-709maxfeed = SND_FXROUND(SND_FXDIV_MAX, align),count = min(free, maxfeed)β free is arbitrary (user-paced), so bad residues β₯ first_bad occur routinely on 8Kβ64K buffers. - Return path:
buffer.c:712sndbuf_acquire(to, tmpbuf, cnt)with cnt > free β ring write past free region,rlinflated pastbufsize(sndbuf_getfree = bufsize - rlgoes unsigned-negative). - 64KB-exact tmpbuf (
buffer.c:226kmalloc(round_page(65536))) makes the T1/T2 write a heap OOB write past the allocation.
In-kernel churn (ES1370, 6ch+7ch vchans, bad-residue pacing) showed no
differential anomaly vs aligned control on this production (no-INVARIANTS)
kernel β see DF-4849 pack churn_*.log. Feeder-level proof is decisive.
Fix
fix.diff β fix the generator (snd_fxdiv_gen.awk) so the magic reciprocal
is exact across the whole claimed domain (grow shift until
(d*mul - 2^z) * 65536 < 2^z) and emit a 64-bit multiply (the required
magics no longer fit a 16-bit multiplier). Exhaustively verified for all 44
table divisors over [1,65536]; all four feeder harnesses (format/matrix/
volume/mixer) go FAILβALL PASS with the regenerated header.
DF-4846 β VERDICT
status: reproduced (feeder level, real unmodified code on the audit guest)
primitive: up to oalignβ1 (β€ 14) byte OOB write past the destination
window + window-preceding underwrite (ialign>oalign misround) + return value
past the caller's count into sndbuf_acquire (ring overflow)
confidence: certain (arithmetic defect exhaustively enumerated; feeder
behavior proven on the pristine file with the exact kernel-generated table)
Root cause
sys/tools/sound/snd_fxdiv_gen.awk:56-71 (calcdiv) validates its magic
reciprocal only at a single point ((y*d) >> z >= 1), not across the claimed
domain x = 1..65536. For every non-power-of-2 divisor the emitted
SND_FXDIV(x,d) equals floor(x/d) + 1 for a large, exhaustively-enumerated
fraction of the domain β e.g. FXROUND(5473,14)=5474, FXROUND(8207,24)=8208
(hand-verified: (5473*2341)>>15 = 391, true floor = 390), first bad x per
align: 12β8195, 14β5473, 15β4694, 24β8207, 28β5487; worst overshoot at the
65536 top: 12β+8, 14β+12, 15β+14. This extends DF-4710 (feeder_eq, which
characterized only the 65536 boundary) to the whole upper half of the domain
β ordinary 8Kβ64K buffer counts, not exotic full-drain events.
feeder_format.c consumes the broken macros on the caller's byte window:
:220count = SND_FXROUND(count, info->oalign)β misround β count' > count. The pass (source ask, in-place conversion tail,dstadvance, and the returneddst - b) is then sized for count' β past the window the caller (sndbuf_feed, buffer.c:708-712) provided.:233j = SND_FXROUND(count, ialign)(ialign>oalign, e.g. S32βS16 multichannel) β misround β j > count βsrc = dst + count - j < dst: the source feeder is asked to fill a window that starts BEFORE the destination buffer.:240j = SND_FXDIV(FEEDER_FEED(...), ialign)β miscount of a partial (user-paced) source return β frame overcount: the converter reads a frame past the delivered data and returns more bytes than the input warrants (over-report into sndbuf_acquire).
Reproduction (guest, pristine file)
fxfmt (see fmt.log): checks=28200 failures=8315, decisive lines:
FAIL[T1 oalign>ialign] ch=5 in=00500010 out=00501000 count=16399 ret=16400 REAR guard (write past window) maxwrite_off=16400 maxask=8200 FAIL[T3 ret-misround over-conversion] ch=7 in=00701000 out=00700010 count=65520 ret=65506 return > delivered*(oalign/ialign) ...
The canary-guarded window is exactly count bytes (front 0xCB / rear 0xCA
guards, checked after every feed); "REAR guard" = the feeder wrote past the
window; ret > count = the return over-reports into sndbuf_acquire.
Kernel reachability (each step verified in-tree)
- Format feeder inserted on any encoding mismatch β
feeder_chain.c:139-173(feeder_build_format), default chain mode, no root gate; the format choice is an unprivilegedSNDCTL_DSP_SETFMT/SNDCTL_DSP_CHANNELSon/dev/dsp. - Head feeder invoked with
count = min(free, maxfeed)wheremaxfeed = SND_FXROUND(SND_FXDIV_MAX, align)(buffer.c:705) β free is arbitrary user-paced, bad residues β₯ first_bad occur on every 8Kβ64K buffer with ~15β40% probability per feed. - Non-pow2 aligns are ordinary multichannel formats: 5ch/6ch/7ch S16/S24/S32 (align 10/12/14/15/20/21/24/28).
- Overshoot lands past
to->tmpbuf(heap OOB when the allocation is exact, buffer.c:226kmalloc(round_page(bufsize))at 64K geometry) and/or inflates the ring (sndbuf_acquire, buffer.c:615-636, KASSERT-only in production;sndbuf_getfree = bufsize - rlwraps unsigned).
In-kernel attempt (honest)
ES1370 + two vchan children (6ch, 7ch S16) + bad-residue-paced writes: no differential anomaly vs the aligned control (production kernel, no INVARIANTS; the write is β€ 14 bytes past a kmalloc and the ring overflow is silent) β identical outcome to DF-4710's in-kernel attempt, and expected for the same reasons. Logs in the DF-4849 pack.
Fix validation
fix.diff (generator): domain-exact reciprocals + 64-bit multiply.
Exhaustive host check: all 44 table divisors exact over [1,65536].
Guest A/B: baseline fxfmt 8315 failures vs fxfmt_fixed 0 failures
(ALL PASS) (fmt.log vs fmt_fixed.log). fix_status: fixed.
Not re-reported
DF-4710 (feeder_eq, root-gated EQ) is the same idiom family in a different file; DF-4601/4602 (feeder_chain) unrelated. This finding covers feeder_format.c only.
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
fixedA/B validated at the level the defect was reproduced: identical harness, only the generated header swapped (baseline table vs fixed-generator table) - 8315 failures vs 0 (fmt.log vs fmt_fixed.log). Fixed table exhaustively verified exact for all 44 divisors over [1,65536]. Kernel rebuild A/B not performed: the in-kernel baseline observation on this rig was null (no INVARIANTS, <=14-byte write past kmalloc has no detector), so a rebuilt kernel had no observable to compare - same standard as DF-4710.
fmt_fixed.log (failures=0 ALL PASS); fix.diff; snd_fxdiv_gen_fixed.awk
Confirmed kernel references
- sys/dev/sound/pcm/feeder_format.c:220
- sys/dev/sound/pcm/feeder_format.c:233
- sys/dev/sound/pcm/feeder_format.c:237
- sys/dev/sound/pcm/feeder_format.c:240
- sys/dev/sound/pcm/feeder_format.c:246
- sys/dev/sound/pcm/feeder_chain.c:139-173
- sys/dev/sound/pcm/buffer.c:705
- sys/dev/sound/pcm/buffer.c:708-712
- sys/dev/sound/pcm/buffer.c:615-636
- sys/dev/sound/pcm/buffer.c:226
- sys/tools/sound/snd_fxdiv_gen.awk:56-71
Detail
Evidence (decisive lines)
fmt.log (baseline: checks=28200 failures=8315, 'REAR guard (write past window)' + 'return > count' + T3 over-conversion lines with exact counts/returns); fmt_fixed.log (fixed table: failures=0 ALL PASS); fxfmt.c/fxshim.h/fxsrc.c (harness compiling the pristine feeder_format.c with the real pcm.h/intpcm.h/g711.h/matrix.h headers and the real awk-generated snd_fxdiv_gen.h); VERDICT.md (full path:line chain)
PoC changes
PoC authored fresh in pass 2 (FXROUND follow-up lead from DF-4710): userland harness compiling the pristine feeder_format.c verbatim (#ifdef _KERNEL-gated file) with real kernel headers copied in and the real generated snd_fxdiv_gen.h. Three harness iterations: (1) arena bounds initially refused guard-region writes, masking violations - fixed to allow writes anywhere inside the guarded allocation so the canaries detect them; (2) source stub did not dry up (ready never decremented) - fixed; (3) volume feeder needed desc->type=FEEDER_VOLUME and matrix ids had to be the real matrix.h values.
Verified recommended fix
Fix snd_fxdiv_gen.awk to emit domain-exact reciprocals ((dmul-2^z)65536 < 2^z) with a 64-bit multiply in SND_FXDIV (fix.diff); optionally also clamp each feeder's count to <= the caller's window.
Verdict
Reproduced at feeder level on the audit guest against the real, unmodified sys/dev/sound/pcm/feeder_format.c: the build-time generated SND_FXDIV/SND_FXROUND table (sys/tools/sound/snd_fxdiv_gen.awk) is inexact WITHIN its claimed [1,65536] domain for every non-power-of-2 divisor (misrounds up from x=4694/5473/8195 for align 15/14/12; 15-40% of all counts), and feed_format_feed() sizes its pass with those macros: :220 FXROUND(count,oalign) can exceed the caller's count so the source writes past the destination window and the return over-reports into sndbuf_acquire (ring overflow, buffer.c:615-636 KASSERT-only); :233 FXROUND(count,ialign) misrounds make j>count so src=dst+count-j lands BEFORE the window; :240 FXDIV of a user-paced partial source return miscounts a frame (over-conversion). Harness on pristine code with the real generated header: 8315/28200 checks fail with canary-guard hits; with the fixed generator (fix.diff: domain-exact reciprocals + 64-bit multiply) 0 failures. In-kernel reachability fully traced (feeder_chain.c:139-173 default insert on any encoding mismatch; buffer.c:705-709 arbitrary free counts; multichannel formats are unprivileged DSP ioctls); in-kernel churn (ES1370, 6ch+7ch vchans, bad-residue pacing) shows no differential anomaly vs aligned control on this no-INVARIANTS production kernel, as expected for a <=14-byte write past a kmalloc plus a silent ring overflow - same outcome and reasoning as DF-4710. Impact ceiling: 4-14-byte kernel heap OOB write past tmpbuf at exact-page allocation geometry + persistent sndbuf ring invariant breakage (rl inflated past bufsize; sndbuf_getfree=bufsize-rl wraps unsigned), unprivileged, default config.
No comments yet.