DragonFlyBSD Kernel Audit
DF-2694 / panic.txt
← back to finding ↓ download raw
panic: assertion "m" failed in sorecvtcp at /usr/src/sys/kern/uipc_socket.c:1852
cpuid = 4
Trace beginning at frame 0xfffff80118677738
sorecvtcp() at sorecvtcp+0x988 0xffffffff806c6e78
sorecvtcp() at sorecvtcp+0x988 0xffffffff806c6e78
kern_recvmsg() at kern_recvmsg+0xf7 0xffffffff806cba17
sys_recvfrom() at sys_recvfrom+0xc4 0xffffffff806cbc64
syscall2() at syscall2() +0x11e 0xffffffff80bd6a0e
Debugger("panic")

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

--------------------------------------------------------------
Captured from dfbsd-qemu serial console (vm.sh log) after the
run:  /tmp/poc_race 40 3000 3000   (as unprivileged user maxx,
uid 1001).  DragonFly 6.5-DEVELOPMENT #0 stock INVARIANTS kernel.

The panic is KKASSERT(m) in the post-copy "sync" block of
sorecvtcp(): after sorecvtcp() released the receive token
(uipc_socket.c:1756/1758) and copied from the M_SOLOCKED mbuf
chain, a concurrent shutdown(fd, SHUT_RD) -> soshutdown() ->
sorflush() (which never takes ssb_lock) acquired the token while
the reader was blocked in a swap-in page fault (blocked DFly
threads release their lwkt tokens: lwkt_switch ->
lwkt_relalltokens), snapshotted the sockbuf, zeroed it and freed
every mbuf via ssb_release() -> sbflush() -> sbdrop() ->
m_freem() (no M_SOLOCKED check anywhere on that path).  When the
reader woke it kept reading the freed mbufs; the copy loop exited
on m == NULL and the sync block then found so_rcv.ssb_mb == NULL
with offset != 0 -> KKASSERT(m) -> panic.

Reproducer: findings/poc/DF-2694/poc_race.c  (see README.md)