โฌข DragonFlyBSD Kernel Audit
DF-0033 / panic.txt
โ† back to finding โ†“ download raw
=== DF-0033 PANIC / CRASH EVIDENCE (this verification session) ===

All from unprivileged user 'maxx' via rfork(RFPROC|RFTHREAD) fdshare racing
fdl_refcount ++ (kern_fork.c:569, p_token) vs -- (kern_descrip.c:2675, fd_spin).

--------------------------------------------------------------------------------
1. INVARIANTS #0 audit kernel โ€” race CONFIRMED (botch panic, ~25s):
--------------------------------------------------------------------------------
(./fdtol_race 25 12 as maxx)
panic with 1 spinlocks held
panic: filedesc_to_refcount botch: fdl_refcount=-1925828443
Debugger("panic")
Stopped at      Debugger+0x7c:  movb    $0,0xbdaf09(%rip)
Trace: fdfree <- fdfree <- exit1 <- sys_exit <- syscall2

The garbage fdl_refcount value (-1925828443 = 0x8D8E0D2B-style bytes) proves the
freed fdtol slot was RECLAIMED BY AN UNRELATED OBJECT before the victim's fdfree
read it back โ€” i.e. cross-type slab reclamation of the UAF'd chunk is occurring.
This reproduces the prior session's panic and confirms the race is LIVE.

--------------------------------------------------------------------------------
2. Non-INVARIANTS (X86_64_NOINV) kernel โ€” race fires SILENTLY, splice fires:
--------------------------------------------------------------------------------
(./exploit ... as maxx, with the sysctl-name2oid reclaim spray)
Fatal trap 12: page fault while in kernel mode
Stopped at      slab_cleanup+0xa4:      movq    (%rdx),%rdx
Trace: slab_cleanup <- slotimer_callback <- softclock_handler

This crash is the slab garbage-collector dereferencing a corrupted free-list
pointer created by a DOUBLE-FREE of the prematurely-freed fdtol chunk.  The
double-free only happens when a surviving peer/child's fdfree reads the reclaimed
chunk with fdl_refcount field == 1 (drives 1->0 -> splice + kfree = double-free).
On a non-INVARIANTS slab the KKASSERT at kern_descrip.c:2627 is compiled out, so
the corruption proceeds silently until the periodic (10s) slab_cleanup dereferences
the corrupted pointer.  This proves the SPLICE PATH IS REACHED (a victim read
refcount=1 and executed the list-splice at kern_descrip.c:2678-2679).

--------------------------------------------------------------------------------
3. SEPARATE interfering race (NOT DF-0033) โ€” exit1 p_peers list walk:
--------------------------------------------------------------------------------
Stopped at      exit1+0x155:    movq    0x3e8(%rdx),%rax
(kern_exit.c:384-390 โ€” p_peers linked-list walk is unsynchronized across
 concurrent rfork(RFPROC|RFTHREAD) peer/child exits; p_peers @ struct proc 0x3e8)

This is a DIFFERENT bug (same CLASS as DF-0033: per-proc p_token fails to
serialize peers sharing a structure) that fires under the same rfork+exit
concurrency the DF-0033 race needs.  It frequently crashed the system before the
DF-0033 splice could be exploited, narrowing the usable concurrency window.

--------------------------------------------------------------------------------
4. kmem monitor โ€” definitive splice-target check:
--------------------------------------------------------------------------------
A libkvm monitor polling sysent[25].sy_call @ 0xffffffff81037360 every 3ms
showed it NEVER changed from sys_geteuid (0xffffffff8062f720) across all runs,
even when the slab_cleanup double-free cascade fired.  Conclusion: the splice
that fired was driven by a NATURALLY-reclaimed chunk (offset-0 byte == 1 by
coincidence, valid pointers at 24/32 by coincidence -> no GPF, but writes to a
wrong address), NOT by the attacker's sysctl-name2oid reclaim.  The attacker's
controlled content never landed in the freed fdtol chunk.  See VERDICT.md
"Grooming failure โ€” root cause" for the slab-architecture analysis.