DragonFlyBSD Kernel Audit
DF-2694 / panic2.txt
← back to finding ↓ download raw
panic #2: assertion "sb->sb_mb == m" failed in sbunlinkmbuf at /usr/src/sys/kern/uipc_sockbuf.c:552
cpuid = 0
Trace beginning at frame 0xfffff80118623708
sbunlinkmbuf() at sbunlinkmbuf+0x109 0xffffffff806c1e09
sbunlinkmbuf() at sbunlinkmbuf+0x109 0xffffffff806c1e09
soreceive() at soreceive+0xbd5 0xffffffff806c6335
kern_recvmsg() at kern_recvmsg+0xf7 0xffffffff806cba17
sys_recvfrom() at sys_recvfrom+0xc4 0xffffffff806cbc64
syscall2() at syscall2+0x11e 0xffffffff80bd6a0e
Debugger("panic")

CPU0 stopping CPUs: 0x0000003e
 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 60 200 500 1    (mode 1 = AF_UNIX SOCK_STREAM
socketpair, unprivileged user maxx).

Same root cause as panic #1, different receive path: generic
soreceive() (used by AF_UNIX / UDP) holds the receive token
across its uiomove copy loop, but a DFly thread that blocks in
tsleep() temporarily releases all its lwkt tokens
(lwkt_switch -> lwkt_relalltokens).  While the reader slept in a
swap-in page fault, shutdown(fd, SHUT_RD) -> soshutdown() ->
sorflush() (which never takes ssb_lock, uipc_socket.c:1953-1957)
acquired the token, bzero()'d the sockbuf and freed every mbuf
(ssb_release -> sbflush -> sbdrop -> m_freem).  The reader woke,
finished copying from a freed cluster, and then hit
sbunlinkmbuf()'s KKASSERT(sb->sb_mb == m) (uipc_sockbuf.c:552)
because sb->sb_mb had been zeroed underneath it.

On a NON-INVARIANTS (production) kernel that KKASSERT is absent:
sbunlinkmbuf() would then write sb->sb_mb = m->m_nextpkt (a stale
pointer read from freed memory) and sbfree() accounting on a freed
mbuf - i.e. sockbuf state corruption with attacker-influenced
reuse, strictly worse than the panic.

panic #1 (TCP path) is in panic.txt.