DF-4580 / verdict.json
{ "finding_id": "DF-4580", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Full trigger (DRM_IOCTL_RADEON_CS with crafted num_chunks) cannot run on the audit guest: it has no AMD/ATI GPU and no /dev/dri, so radeon_cs_ioctl is unreachable there. The code path is certain by inspection: radeon_cs_parser_init sizes wired GFP_KERNEL allocations from the uncapped u32 num_chunks (8n bytes at radeon_cs.c:287, 24n at :298, 4*length_dw at :346), and DragonFly maps GFP_KERNEL to M_WAITOK WITHOUT M_NULLOK (gfp.h:40), so allocation failure is panic(M_DRM: malloc limit exceeded) at kern_slaballoc.c:877 (num_chunks ~40-50M pushes the first 320-400MB alloc over the physmem/10 type limit, next kcalloc panics) or an eternal unkillable vm_wait() loop (kern_slaballoc.c:1780-1806) for num_chunks=0xFFFFFFFF (32GB request > physmem). The sink mechanism was independently REPRODUCED on the guest with a kld performing the identical M_WAITOK|M_ZERO no-M_NULLOK allocation pattern: panic df4580: malloc limit exceeded at alloc #13 (416MB cumulative vs 389MB limit), backtrace _kmalloc -> df4580_modevent (panic.txt, run.log). Additionally the Linux-shaped NULL-return path would NULL-deref parser->chunks[i] in radeon_cs_parser_fini (radeon_cs.c:447-448) with chunks==NULL/nchunks>0; on DF as compiled this is masked by the allocation panic (kmem_slab_alloc never returns NULL without M_NULLOK) but becomes live if allocator flags are fixed without also fixing fini.", "exploit_chain": "", "evidence": "panic.txt + run.log: df4580: alloc #13 (type usage ~416 MB) then panic: df4580: malloc limit exceeded with _kmalloc backtrace = the exact sink radeon_cs.c:287/298 rides; VERDICT.md section Code trace of the trigger for the ioctl-side chain; build.log for the kld build; fix.diff applies cleanly (git apply --check).", "kernel_refs": [ "sys/dev/drm/radeon/radeon_cs.c:287", "sys/dev/drm/radeon/radeon_cs.c:298", "sys/dev/drm/radeon/radeon_cs.c:346", "sys/dev/drm/radeon/radeon_cs.c:447", "sys/dev/drm/include/linux/gfp.h:40", "sys/kern/kern_slaballoc.c:877", "sys/kern/kern_slaballoc.c:1724", "sys/kern/kern_slaballoc.c:1780" ], "poc_changes": "No PoC seed existed; authored from scratch. kld module reproduces the DF GFP_KERNEL allocation-failure sink (M_WAITOK|M_ZERO, no M_NULLOK, own malloc type with the standard physmem/10 limit) rather than the ioctl path, because the guest lacks AMD GPU hardware. One build fix: DECLARE_MODULE argument (moduledata_t, not handler).", "attempts": 2, "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": 210, "guest_dirty": 0, "build_cmd": "cd /root/df4580 && make", "run_cmd": "kldload /root/df4580/df4580.ko (expect: panic df4580: malloc limit exceeded)", "code_hash": "cb6de8594a0f5023455e8204653c4d0afb9a22abc0ea785613d1db248bc96fa1", "notes": "Guest has no AMD GPU (pciconfig -l has no 1002: device; /dev/dri absent) -> radeon_cs_ioctl itself not testable on this guest; sink mechanism proven instead. Guest panicked by design during verification and was reset to the with-src snapshot afterwards (module no longer loaded). Fix validation not runnable for the same hardware reason.", "recommended_fix": "Cap num_chunks (<=64) and per-chunk length_dw (<=1M dwords) in radeon_cs_parser_init before allocating, and NULL-guard the parser->chunks loop in radeon_cs_parser_fini.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored and git-apply-check clean, but baseline/patched behavior cannot be exercised on this guest (no radeon hardware); the mechanism kld is unaffected by the radeon-side fix.", "fix_evidence": "findings/poc/DF-4580/fix.diff (3 hunks: num_chunks cap, length_dw cap, fini NULL-guard); git apply --check passes." } |