DF-2866 / verdict.json
{ "finding_id": "DF-2866", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Reproduced the dispatch primitive on the stock INVARIANTS guest: enum obj_type has 8 values (OBJT_MARKER==7, vm_object.h:118-127) but pagertab[] has only 7 entries (vm_pager.c:152-160), and every dispatcher (vm_pager_deallocate vm_pager.c:340; vm_pager_get_page/put_pages/has_page inlines vm_pager.h:134/150/168) indexes pagertab[object->type] unbounded. KLD stage 1 dumped pagertab[7] (the OOB slot: NULL on this link layout - whatever the linker placed after the array); stage 2 called vm_pager_has_page() on a type=OBJT_MARKER object, producing 'Fatal trap 12: page fault while in kernel mode, fault virtual address = 0x18, movq 0x18(%rax),%rax' - the inline loaded pagertab[7] and dereferenced ->pgo_haspage at +24; guest down at db>. IN-TREE REACHABILITY IS LATENT: all three marker producers (vm_object.c:1847/1960, swap_pager.c:2171/2180, vm_swapcache.c:220/728) check the type before dispatching, so the PoC constructs the marker itself (synthetic trigger) - the finding is the missing bounds/eighth slot (DF-0944 family), rated Low; on a layout where a non-zero word follows the array the same path is a call through an uncontrolled pointer. fix.diff hunk 1 (eighth slot = &deadpagerops) validated on rebuilt kernel #1: dispatch returns FALSE, 'NO PANIC', clean unload.", "exploit_chain": "", "evidence": [ "run.log: 'PGTMARK: pagertab[7] = 0 <-- OOB SLOT' then 'dispatching OBJT_MARKER(7) ... pagertab[7] = 0' and ssh death", "panic.txt: 'Fatal trap 12: page fault while in kernel mode / fault virtual address = 0x18 / Stopped at pgtmark_fire_sysctl.part.0+0x53: movq 0x18(%rax),%rax'", "run.patched.log (kernel #1 with fix.diff): 'pagertab[7] = 0xffffffff81121380 <-- now &deadpagerops (== pagertab[6])' + 'vm_pager_has_page returned 0 -- NO PANIC (fixed kernel)' + clean kldunload", "build.log / fix_build.log: module build and full nativekernel build logs" ], "kernel_refs": [ "sys/vm/vm_pager.c:152", "sys/vm/vm_pager.c:160", "sys/vm/vm_pager.c:337", "sys/vm/vm_pager.c:340", "sys/vm/vm_pager.h:128", "sys/vm/vm_pager.h:134", "sys/vm/vm_pager.h:150", "sys/vm/vm_pager.h:168", "sys/vm/vm_object.h:118", "sys/vm/vm_object.h:126", "sys/vm/swap_pager.c:2171", "sys/vm/vm_swapcache.c:220", "sys/vm/vm_object.c:1847", "sys/vm/vm_object.c:1960" ], "poc_changes": "PoC authored from scratch (no seed). Iterations: (1) removed a speculative pre-dereference of pagertab[7]+24 from the kprintf so any fault lands inside the dispatcher where it belongs; (2) fire hooked through SYSCTL_PROC handler instead of plain SYSCTL_INT so the dispatch runs on demand, not at load.", "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": 20, "guest_dirty": 0, "build_cmd": "sh build.sh (KMOD=pgtmark built in-guest against /usr/src/sys)", "run_cmd": "sh run.sh (kldload pgtmark.ko; dmesg | tail; sysctl vm.pgtmark_fire=1 -> panic on stock kernel)", "code_hash": "0cf918414ea7b126beee0ed06e0c696b1828c636f29ed69a1faa3675b63b5fa0", "notes": "Trigger is synthetic by design (no in-tree path dispatches a marker today) - this is the DF-0944 latent-dispatch family; the PoC proves the missing bounds check and its effect when hit, not an existing attacker path. fix.diff hunks 2-6 are the DF-2865 cmpxchg fix; both were validated on one combined nativekernel build. Guest panicked for the baseline capture, then reset with-src; after fix validation guest was reset with-src again and is up on stock kernel #0.", "recommended_fix": "Add the missing eighth slot '&deadpagerops /* OBJT_MARKER (list-scan marker; never paged) */' to pagertab[] so any marker-typed object dispatched to a pager op lands in the dead pager instead of one past the array - see fix.diff hunk 1.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 14:41:38 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 (hunk 1 = this fix; hunks 2-6 = DF-2865) to /usr/src in-guest, make -j6 nativekernel KERNCONF=X86_64_GENERIC + installkernel, rebooted into kernel #1. Exact same fire sysctl: pagertab[7] resolves to &deadpagerops (== pagertab[6]), vm_pager_has_page returns 0, 'NO PANIC', module unloads, guest stays up.", "fix_evidence": [ "findings/poc/DF-2866/run.patched.log: 'PGTMARK: pagertab[7] = 0xffffffff81121380' + 'returned 0 -- NO PANIC (fixed kernel)' + 'UNLOAD-OK'", "findings/poc/DF-2866/panic.txt: baseline stock-kernel Fatal trap 12 at 0x18 for contrast", "findings/poc/DF-2866/fix.diff: git-apply-able (verified --check against pristine sys/)" ] } |