DF-4672 / verdict.json
{ "finding_id": "DF-4672", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED. Unprivileged (uid 1001) METEORSETGEO grows through the production, unmodified video_ioctl of the stock bktr.ko (driven via the DF-1215-style KLD rig /dev/bktrig0 on the hardware-less guest) permanently leaked 52,137 wired pages (203.7 MiB) in one run and 107,297 pages (419.1 MiB) on a fresh guest, with the two DragonFly leak phases visible: silent 256 MiB low-phys DMA-reserve alist drain (wire flat), then PQ-scan allocations wired per grow and never returned (wire climbs, never drops, survives kldunload), ending in get_bktr_mem/contigmalloc exhaustion (ENOMEM). Bare kmem_free of the contigmallocd object-less region returns neither the wired pages nor the reserve block nor removes the kernel PTEs -- exactly the misuse sys/vm/vm_kern.c:311-313 warns about.", "exploit_chain": "open(/dev/bktr0 O_RDONLY) [0444, unpriv] -> ioctl(METEORSETGEO, strictly larger geometry) xN [no priv_check in driver] -> every grow get_bktr_mem()s a new buffer and bare-kmem_free()s the old one -> reserve blocks + wired pages permanently lost -> repeat until the 256 MiB DMA reserve and/or all reclaimable memory is wired -> system-wide allocation exhaustion DoS (verified ENOMEM from get_bktr_mem after 266 MiB of unpriv growth).", "evidence": "run.log: wire delta (permanent): 52137 pages (203.7 MiB) + flat->climb transition lines (WIRE CLIMB PHASE BEGINS, +28234); re-confirm on fresh guest in VERDICT.md; fix_run.log on DF467XFIX kernel: same attack, 85.3 MiB allocated, wire delta 22 pages == noise (leak gone).", "kernel_refs": [ "sys/dev/video/bktr/bktr_core.c:1667", "sys/dev/video/bktr/bktr_core.c:1669", "sys/dev/video/bktr/bktr_os.c:399", "sys/dev/video/bktr/bktr_os.c:403", "sys/vm/vm_contig.c:447", "sys/vm/vm_contig.c:527", "sys/vm/vm_contig.c:588", "sys/vm/vm_kern.c:311", "sys/vm/vm_map.c:3300", "sys/vm/vm_page.c:2765", "sys/vm/vm_page.c:2858", "sys/vm/vm_page.c:426" ], "poc_changes": "Seed bktr_wireleak.c expected an immediate wire climb and hard-coded /dev/bktr0; rewritten as phase-aware rig PoC: /dev/bktrig0, kernel-verified strictly-larger grows, per-grow wire deltas, two-phase detection (reserve drain then PQ wire climb), exact leak prediction. Discovered en route: DragonFly contigmalloc serves from a 256 MiB low-phys DMA reserve first (vm_page.c:2765), so the seeds one-phase model was wrong on this kernel.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (baseline) / #0: Thu Oct 15 00:54:24 UTC 2026 DF467XFIX (fix)", "runtime_sec": 4980, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o poc_wireleak poc_wireleak.c ; make -m /usr/src/share/mk SYSDIR=/usr/src/sys (KLD rig)", "run_cmd": "./run.sh (root: rig load + RIG_INIT mirror; maxx: ./poc_wireleak 128 80)", "code_hash": "539bd1717e52582ec228c72039498d81e3d9c047a06d2be865eb395a0171c590", "notes": "Hardware-gated on real /dev/bktr0 (needs a bt848 card OR the module-loadable path with vfs.devfs rules); the production code path executed is byte-identical to hardware (production video_ioctl + get_bktr_mem/contigmalloc/kmem_free), only the initial buffer install is the rigs attach mirror (production get_bktr_mem + bzero per bktr_core.c:429-432). Residual kernel-map resident_count mistabulation from the missing pmap removal also source-proven (vm_map.c:3300-3370).", "recommended_fix": "Release the grow-path buffer with contigfree() (not bare kmem_free), install the new buffer before freeing the old one, and bzero the new buffer (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fixed", "fix_evidence": "fix_run.log: DF467XFIX kernel (bktr built in, fix applied, KERN_RC=0/INST_RC=0, booted #0 Oct 15 00:54:24 UTC 2026): same unpriv grow storm, 85.3 MiB of frame buffers allocated, permanent wire delta 22 pages (== idle noise of 288) vs +52,137 pages baseline; reserve no longer drains (grow KVA reuse safe)." } |