DF-2776 / verdict.json
{ "finding_id": "DF-2776", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "Reproduced on the stock INVARIANTS guest. The in-kernel rig (sysctl dfh.run2776) registered an intrec via the real register_int, freed it with the real unregister_int, reclaimed the freed 56-byte intrec with an immediate same-cpu kmalloc (reuse=YES, identical address printed), filled the cookie memory with intr=253 and name=&token, then called the real unregister_int with the stale cookie. The kernel read intr from the freed cookie at kern_intr.c:425 (the walk used intr 253, sourced solely from freed memory) and, on the not-found path, printed warning: unregister_int: int 253 handler for DF2776-STALE-COOKIE-READ-4b77d1 not found, disclosing the string at the chosen kernel address to the console/dmesg (kern_intr.c:489-490). The wild variant (name=0x5151515151515150) panicked inside the kprintf: the console shows the message truncated mid percent-s followed by Stopped at strlen cmpb 0,(rdi). Threat model: requires a kernel-context caller to pass a stale cookie (driver double-unregister style bug); unprivileged users cannot trigger it directly, so the ceiling is console kernel-memory disclosure plus a kernel panic (DoS) from an already-privileged code path; severity Low as filed. Fix validated on a rebuilt kernel: the not-found path prints the pointer only (warning: unregister_int: int handler 0xfffff8004f164290 not found), never dereferences the cookie, and the wild variant returns cleanly with the guest alive.", "exploit_chain": "", "evidence": [ "df2776_groomed.dmesg: reuse=YES probe then warning: unregister_int: int 253 handler for DF2776-STALE-COOKIE-READ-4b77d1 not found", "panic.txt: warning: unregister_int: int 253 handler for Stopped at strlen cmpb 0,(rdi) -- kprintf died mid percent-s on the poisoned stale name pointer", "df2776_fixed.dmesg: pointer-only message for both groomed and wild variants, guest still alive", "run.log: full stock and fixed A/B transcript" ], "kernel_refs": [ "sys/kern/kern_intr.c:425", "sys/kern/kern_intr.c:440", "sys/kern/kern_intr.c:489", "sys/kern/kern_intr.c:490" ], "poc_changes": "Built the rig into the in-kernel harness df_harness.c (runtime kldload GPFs on this guest); the reclaim probe writes fake cookie fields only into memory it owns (successful LIFO kmalloc reclaim, verified by printed address equality).", "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": 900, "guest_dirty": 0, "build_cmd": "cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC (stock kern_intr.c + df_harness.c)", "run_cmd": "sysctl dfh.wild2776=0 dfh.run2776=1 ; sysctl dfh.wild2776=1 dfh.run2776=1", "code_hash": "15da5b1aa9b39d592f02f878101e81f28d056fc27f28823a6ec9b392054e2cbe", "notes": "The intr load at kern_intr.c:425 is unavoidable by API design (cookie is the only locator); the fix removes the print-through of the stale pointer which is the disclosure and crash vector. Guest reset with-src after evidence collection.", "recommended_fix": "Print the cookie pointer, never dereference it, on the not-found path of unregister_int (fix.diff hunk: kprintf warning: unregister_int: int handler %p not found)." } |