DragonFlyBSD Kernel Audit
DF-2691 / run.log
← back to finding ↓ download raw
run.log - DF-2691 decisive run #1 (fresh boot, unprivileged user t1)

Command:  su -l t1 -c /tmp/t1home/kqsig_uaf
Setup:    sysctl kern.ps_arg_cache_limit=8192 (debug knob, allows large
          argv to be cached in p_args for the content-controlled recycle
          demonstration; the trigger itself needs no privileges)
Guest:    DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC (INVARIANTS), 6 vCPU

stdout:
[64 dangling sig-knotes attached to freed procs]
[400 p_args spray children exec'd, argv=1251 bytes]
closing kqueue...
<guest dies - no SURVIVED line>

serial console (panic.txt):
Fatal user address access from kernel mode from kqsig_uaf at ffffffff8063c973

Fatal trap 12: page fault while in kernel mode
cpuid = 1; lapic id = 1
fault virtual address	= 0x18
fault code		= supervisor read data, page not present
instruction pointer	= 0x8:0xffffffff8063c973
current process		= 4104
kernel: type 12 trap, code=0
CPU1 stopping CPUs: 0x0000003d
 stopped
Stopped at      knote_remove+0x33:      movq    0x18(%rdx),%rax
db>

0x18 == offsetof(struct knote, kn_next): this is the SLIST_REMOVE walk
`while (SLIST_NEXT(curelm, kn_next) != elm)` with curelm == NULL, i.e.
SLIST_FIRST(&victim->p_klist) == NULL because the freed struct proc chunk
was recycled as a fresh (M_ZERO) process.  The kernel reached this code
from close(kq) -> kqueue_close -> knote drain -> filt_sigdetach ->
knote_remove(&kn->kn_ptr.p_proc->p_klist, kn) with p_proc pointing at
memory freed by kern_wait()'s kfree(p, M_PROC).

Result: reproduced, impact=panic (unprivileged, deterministic enough to
hit on the first run both times), class=UAF (write into freed memory on
the un-recycled path, wild pointer walk on the recycled path).