DragonFlyBSD Kernel Audit
DF-0725 / panic.txt
← back to finding ↓ download raw
DF-0725 if_clone_destroy UAF / double-free-unit race — panic signature
======================================================================

Guest: DragonFly 6.5-DEVELOPMENT #0 (unpatched audit-source, X86_64_GENERIC, INVARIANTS ON)
Trigger: ./race_destroy 12 5000   (12 root processes racing SIOCIFDESTROY gif666)
Mechanism: two concurrent if_clone_destroy() callers both pass ifunit() with the
  same live ifp (ifnet_lock dropped at if_clone.c:112 before the ifp->if_dname
  deref at :116), then both enter the lock-protected critical section at :126.
  The second one's if_clone_free_unit() re-clears an already-cleared bitmap bit
  -> KKASSERT "bit is already cleared" (if_clone.c:367-368) -> panic.

Serial console (dfbsd-qemu/boot.log):

panic: if_clone_free_unit: bit is already cleared
cpuid = 5
Trace beginning at frame 0xfffff801186a9768
if_clone_free_unit.isra.1() at if_clone_free_unit.isra.1+0x49 0xffffffff80733f29 
if_clone_free_unit.isra.1() at if_clone_free_unit.isra.1+0x49 0xffffffff80733f29 
if_clone_destroy() at if_clone_destroy+0x7c 0xffffffff807341fc 
ifioctl() at ifioctl+0x243 0xffffffff80731d73 
mapped_ioctl() at mapped_ioctl+0x5fa 0xffffffff806a831a 
syscall2() at syscall2+0x11e 0xffffffff80bd6a0e 
Debugger("panic")

CPU5 stopping CPUs: 0x0000001f
 stopped
Stopped at      Debugger+0x7c:  movb    $0,0xbdaf09(%rip)
db> 

Interpretation:
- With INVARIANTS ON (default GENERIC): the KKASSERT at if_clone.c:367 fires on the
  second destroyer's redundant unit-bit clear -> deterministic panic (this trace).
- With INVARIANTS OFF: the second destroyer proceeds to ifc->ifc_destroy(ifp) at
  :128 on the ifp that the first destroyer already freed (gif_clone_destroy -> if_detach
  -> kfree ifp) -> use-after-free (silent heap corruption / secondary panic).

Reachability: SIOCIFDESTROY is gated by caps_priv_check(SYSCAP_RESTRICTEDROOT)
  (sys/net/if.c:2013); tun/tap auto-destroy-on-close is also root-gated
  (tunopen:283, tapopen:324 both require SYSCAP_RESTRICTEDROOT). So the race is
  root-triggerable only -> root->kernel DoS / hardening gap (no unpriv path).