DF-2859 / verdict.json
{ "finding_id": "DF-2859", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "sglist_split()'s trim bcopy (sys/kern/subr_sglist.c:571-574) moves `count` entries instead of the surviving `original->sg_nseg` entries, deterministically corrupting the survivor list (duplicated segments, dropped tail โ wrong DMA ranges for any consumer) and heap-overreading up to (2*count - sg_maxseg)*16 bytes past the kmalloc'd segs array when survivors < count. Reproduced 3/3 on the stock guest via a KLD library harness (cases A/B); OOB-read mode by construction (case C). sglist_split has zero callers in the DragonFly tree, so the realistic impact ceiling today is none (severity Low, dead code, same framing as DF-0096); the identical bug is still present in FreeBSD HEAD. One-line fix validated by in-guest kernel rebuild: baseline BUG REPRODUCED -> patched PASS.", "exploit_chain": "N/A โ dead code (no in-tree caller of sglist_split). If a consumer is ever added: split a list so survivors != count -> survivor sglist contains stale/duplicated (paddr,len) pairs -> device DMAs wrong physical memory (read of unrelated physical pages into the transfer buffer / write of transfer data to unrelated physical pages) plus heap OOB read in over-copy mode.", "evidence": [ "run.log: 'A.orig.after seg[0..1] paddr=0x130000' duplicated, 'verdict: BUG REPRODUCED' (baseline run 1)", "run.2.log / run.3.log: same corruption on runs 2 and 3 (deterministic)", "fix_run.log: patched kernel -> A.orig.after = P1..P5 distinct, B.orig.after = [0x130800/0x800, P2..P5], 'verdict: PASS'", "fix_build.log: full nativekernel build log ending BUILD_OK (kernel #1 Wed Sep 2 13:15:59 UTC 2026)", "VERDICT.md: full narrative incl. dead-code reachability analysis and upstream parity check" ], "kernel_refs": [ "sys/kern/subr_sglist.c:571", "sys/kern/subr_sglist.c:573", "sys/kern/subr_sglist.c:563", "sys/kern/subr_sglist.c:206", "sys/sys/sglist.h:105" ], "poc_changes": "no seed existed; KLD harness authored from scratch (runs at MOD_LOAD via exported sglist_alloc/append_phys/split symbols; three deterministic cases A/B/C)", "attempts": 3, "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": 640, "guest_dirty": 0, "build_cmd": "cd /root/sgdemo && make", "run_cmd": "kldload /root/sgdemo/sgsplit_demo.ko; sleep 1; dmesg | grep DF2859; kldunload sgsplit_demo", "code_hash": "4834dc2ff2ded1ffc3efda4f6ea2d0548649da4cc6b054f8305f3cecc63bebcb", "notes": "Dead code: sglist_split has no callers in the DF tree; severity Low per DF-0096 precedent. Upstream FreeBSD HEAD (checked 2026-09-02) has the identical `count *` trim block. Live sglist surface in DF = virtio append/alloc/free family only. Phase V for DF-2860/DF-2861 skipped: Low/Info dead-code hardening findings per contract.", "recommended_fix": "sglist_split: bcopy the surviving remainder โ `original->sg_nseg * sizeof(struct sglist_seg)` โ instead of `count *` (see fix.diff)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 13:15:59 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Applied fix.diff in guest /usr/src, make -j6 -DNO_CLEAN nativekernel + installkernel (fix_build.log, BUILD_OK), rebooted into kernel #1, re-ran the byte-identical sgsplit_demo.ko: all cases PASS, verdict line PASS, previously-observed duplicated/dropped segments GONE.", "fix_evidence": "fix_run.log (patched PASS output incl. correct [P1..P5] and [0x130800/0x800, P2..P5] survivor lists); fix_build.log (full build log, BUILD_OK)" } |