⬢ DragonFlyBSD Kernel Audit
DF-4849 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4849",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Reproduced at feeder level on the audit guest against the real, unmodified sys/dev/sound/pcm/feeder_mixer.c: (M1) :299 count = SND_FXROUND(count, sz) misrounds PAST the callers free space for bad-residue values inside the claimed domain (first bad 8195 for sz=12; 15-40% of values above first_bad), children fill count > free bytes into b and the mixer returns rcnt > free - in-kernel sndbuf_acquire() (buffer.c:615-636, KASSERT-only) then overflows the ring and inflates rl past bufsize so sndbuf_getfree = bufsize - rl wraps unsigned. 500/500 M1 checks fail. (M2) :339-364 FXROUNDs each childs return; with a child feeder that returns more than asked (proven in-tree on real code by DF-4846 feeder_format and DF-4848 feeder_volume), rcnt > count makes :362 mcnt = count - rcnt UNDERFLOW and :368 executes memset(b + rcnt, zerodata, ~4GiB) - harness caught it verbatim: MEMSET-BOMB: memset(b + 65556, 0x00, 4294967284) (exit 42). Vchan mixing is default (snd_maxautovchans=16, sound.c:62) - any two /dev/dsp opens mix through this path with parent sz set by unprivileged channel ioctls. In-kernel churn (ES1370, 6ch+7ch children, bad-residue pacing) showed no differential anomaly vs the aligned control (channel dead occurs in both - ES1370-without-audiodev artifact), so no in-kernel observation on this no-INVARIANTS rig; the memset would be instantly fatal if its composite conjunction fired.",
  "exploit_chain": "unprivileged: two /dev/dsp opens (two vchan children on one default-mixed parent) -> multichannel formats give non-pow2 parent sz (e.g. 6ch S16 = 12) -> user-paced drains land free on bad residues >= 8195 -> persistent ring overflow (audio corruption + getfree wrap); with an overshooting child head feeder (feeder_format/feeder_volume per DF-4846/4848) the mcnt underflow arms a ~4GiB kernel memset 12 bytes past the destination scratch - total kernel memory destruction.",
  "evidence": "mix.log (M1 500/500 rcnt=8196 (+1 bytes past free) + MEMSET-BOMB: memset(b + 65556, 0x00, 4294967284), exit 42); mix_fixed.log (fixed: 0 failures, no bomb); churn_bad.log + churn_dmesg.txt (in-kernel attempt, non-differential); ffmix.c/fxshim.h/fxsrc.c; VERDICT.md",
  "kernel_refs": [
    "sys/dev/sound/pcm/feeder_mixer.c:299",
    "sys/dev/sound/pcm/feeder_mixer.c:339-364",
    "sys/dev/sound/pcm/feeder_mixer.c:362",
    "sys/dev/sound/pcm/feeder_mixer.c:368",
    "sys/dev/sound/pcm/buffer.c:708-712",
    "sys/dev/sound/pcm/buffer.c:615-636",
    "sys/dev/sound/pcm/sound.c:62",
    "sys/tools/sound/snd_fxdiv_gen.awk:56-71"
  ],
  "poc_changes": "Fresh harness (DF-4846 infrastructure) extended with fake struct pcm_channel/snd_dbuf machinery (children.busy list, CHN_FOREACH/CHN_LOCK/CHN_STOPPED, per-child feed control, sndbuf_* shims) and a memset interceptor defined before including the file under test so the ~4GiB call is recorded (exit 42) instead of executed; M2 child overshoot (+8) is the delta class fxfmt produces from real feeder_format code (count 65548 -> 65556).",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "runtime_sec": 900,
  "guest_dirty": 0,
  "build_cmd": "cd /root/fx && sh build.sh; cc -O1 -g -o ffmix ffmix.c fxsrc.c",
  "run_cmd": "./ffmix   # baseline: exit 42 (memset bomb) + M1 fails; cp snd_fxdiv_gen_fixed.h snd_fxdiv_gen.h; rebuild -> ALL PASS",
  "code_hash": "",
  "notes": "Guest booted with -device ES1370 and snd_es137x kldloaded for the churn; reset to clean-source snapshot afterwards. The M2 composite precondition (child feeder returning count+delta) is separately proven on real code - DF-4846 (feeder_format T1: ret=16400 for count=16399) and DF-4848 (feeder_volume T1: ret=32772 for count=32771). Shared table root cause with DF-4846/4847/4848; one generator fix clears all four.",
  "recommended_fix": "Fix snd_fxdiv_gen.awk (domain-exact reciprocals + 64-bit multiply, fix.diff); independently harden :362 with if (rcnt > count) rcnt = count; so no feeder-contract violation can arm the giant memset.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "A/B at harness level, only the generated header swapped: baseline = 500/500 M1 ring-overflow fails + memset bomb (exit 42); fixed = 0 failures, no bomb (mix.log vs mix_fixed.log). Kernel rebuild A/B not performed (null differential in-kernel baseline on this rig).",
  "fix_evidence": "mix_fixed.log (0 failures, no bomb); fix.diff; snd_fxdiv_gen_fixed.awk"
}