DragonFlyBSD Kernel Audit
DF-0033 / panic.txt
← back to finding ↓ download raw
=== DF-0033 PANIC EVIDENCE (from dfbsd-qemu/boot.log, serial console) ===

Three distinct kernel panics were observed from unprivileged user 'maxx' running
fdtol_race (rfork(RFPROC|RFTHREAD) peers + concurrent child exit). All three are
manifestations of the same root cause: the lost-update race on the plain-int
fdtol->fdl_refcount (incremented under per-proc p_token in fork1, decremented
under the shared fd_spin in fdfree) -> premature free of fdtol -> UAF.

The race is intermittent (CVSS AC:H); reproducing typically needs several 20-60s
bursts of hammering. All three panics below occurred across burst-loop runs.

--------------------------------------------------------------------------------
PANIC A (DEFINITIVE) -- the KASSERT at kern_descrip.c:2627-2629 catching the
refcount invariant violation directly. fdl_refcount underflowed to 0 while a
process still held a live reference and was entering fdfree.
--------------------------------------------------------------------------------
login: panic with 1 spinlocks held
panic: filedesc_to_refcount botch: fdl_refcount=0
cpuid = 1
Trace beginning at frame 0xfffff800ab95f728
fdfree() at fdfree+0x6f2 0xffffffff806331c2 
fdfree() at fdfree+0x6f2 0xffffffff806331c2 
exit1() at exit1+0x13f 0xffffffff8063ef6f 
sigexit() at sigexit+0x4c 0xffffffff8066091c 
postsig() at postsig+0x44a 0xffffffff8066173a 
userret() at userret+0x28b 0xffffffff80bd50bb 
Debugger("panic")

CPU1 stopping CPUs: 0x00000001
 stopped
Stopped at      Debugger+0x7c:  movb    $0,0xbd77f9(%rip)
db> 

This is the exact KASSERT:
    sys/kern/kern_descrip.c:2627  KASSERT(fdtol->fdl_refcount > 0,
    sys/kern/kern_descrip.c:2628      ("filedesc_to_refcount botch: fdl_refcount=%d",
    sys/kern/kern_descrip.c:2629       fdtol->fdl_refcount));
firing with fdl_refcount=0 -- i.e. the decrement side drove the shared
refcount word below the true reference count because a concurrent increment
(kern_fork.c:569) was lost (the two sides hold different, non-mutual locks).

--------------------------------------------------------------------------------
PANIC B -- slab double-free detection. After the premature kfree(fdtol) in
fdfree, a peer's dangling p_fdtol reference caused a second free of the same
M_FILEDESC_TO_LEADER object -> the slab allocator's BADFREE2 check fires.
--------------------------------------------------------------------------------
login: panic: BADFREE2
cpuid = 0
Trace beginning at frame 0xfffff800ab92c6f8
_kfree() at _kfree+0x593 0xffffffff80657b23 
_kfree() at _kfree+0x593 0xffffffff80657b23 
sysctl_kern_proc_args() at sysctl_kern_proc_args+0x475 0xffffffff8064ebf5 
sysctl_root.isra.5() at sysctl_root.isra.5+0x208 0xffffffff806834b8 
userland_sysctl() at userland_sysctl+0x111 0xffffffff806837b1 
sys___sysctl() at sys___sysctl+0x74 0xffffffff80683914 
Debugger("panic")

CPU0 stopping CPUs: 0x00000002
 stopped
Stopped at      Debugger+0x7c:  movb    $0,0xbd77f9(%rip)
db> 

(The sysctl_kern_proc_args frame is collateral: the UAF corrupted slab metadata
of a neighboring allocation in the same zone; the slab's free-list bookkeeping
became inconsistent, so a later kfree in an unrelated path trips BADFREE2.)

--------------------------------------------------------------------------------
PANIC C (SMOKING GUN) -- slab corruption detected on the NEXT allocation out of
the M_FILEDESC_TO_LEADER zone. The stack walks the EXACT functions cited in the
finding: filedesc_to_leader_alloc <- fork1 <- sys_rfork.
--------------------------------------------------------------------------------
login: panic: memory chunk 0xfffff800461052ad is already allocated!
cpuid = 0
Trace beginning at frame 0xfffff800ab68b7c8
chunk_mark_allocated() at chunk_mark_allocated+0x9e 0xffffffff8065556e 
chunk_mark_allocated() at chunk_mark_allocated+0x9e 0xffffffff8065556e 
_kmalloc() at _kmalloc+0x684 0xffffffff80656b54 
filedesc_to_leader_alloc() at filedesc_to_leader_alloc+0x23 0xffffffff80633e43 
fork1() at fork1+0xbe9 0xffffffff80642be9 
sys_rfork() at sys_rfork+0x43 0xffffffff80642d73 
Debugger("panic")

CPU0 stopping CPUs: 0x00000002
 stopped
Stopped at      Debugger+0x7c:  movb    $0,0xbd77f9(%rip)
db> 

This confirms the full chain end-to-end:
  1. refcount race (kern_fork.c:569 ++ under p_token | kern_descrip.c:2675 -- under fd_spin)
  2. premature kfree(fdtol) in fdfree (kern_descrip.c:2686)
  3. dangling p_fdtol -> writes into the freed 64-byte-zone slot -> slab corruption
  4. next filedesc_to_leader_alloc() (kern_descrip.c:3352) -> _kmalloc -> slab sees
     the corrupted chunk as "already allocated" -> panic