DF-0735 / trigger_df735.sh
#!/bin/sh # DF-0735 trigger -- root-only. # # Builds sys/netgraph7/ng_ipfw.c into ng_ipfw.ko, loads it + its dependencies, # sets up an ipfw ngtee divert rule (rule 1 -> ipfw ng node hook "1"), then # sends one ICMP packet to ourselves. ipfw calls ng_ipfw_input (the function # registered at MOD_LOAD in ng_ipfw.c:118) which tags the packet and queues # it to the ipfw node's hook. The netgraph worker thread picks up the queued # item and calls ng_ipfw_rcvdata (ng_ipfw.c:220) -> reads the tag (line 228) # -> for dir=NG_IPFW_IN (which is what 'ngtee ... in' produces) calls # ip_input(m) at line 248 DIRECTLY from the ng worker thread -> the # ASSERT_NETISR_NCPUS(mycpuid) at ip_input.c:460 fires -> kernel panic. # # Required: ng_ipfw is NOT built by default (gated on `optional netgraph7_ipfw` # in sys/conf/files and absent from sys/netgraph7/Makefile SUBDIR). Building # and loading it requires root. # # Usage: ./trigger_df735.sh (run on the guest as root) # Output: exits 0 after sending the packet; the panic lands in the serial # boot log (kernel side), not on stdout. set -eu SRC=/usr/src/sys/netgraph7 WRK=${WRK:-/root/df735_wrk} echo "[*] preparing work dir: $WRK" rm -rf "$WRK"; mkdir -p "$WRK" cp "$SRC/ng_ipfw.c" "$SRC/ng_ipfw.h" "$WRK/" cat > "$WRK/Makefile" <<'EOF' KMOD= ng_ipfw SRCS= ng_ipfw.c SYSDIR?= /usr/src/sys .include <bsd.kmod.mk> EOF echo "[*] building ng_ipfw.ko from in-tree source (unchanged)" cd "$WRK" make -m /usr/share/mk obj 2>&1 | tail -3 || true make -m /usr/share/mk KMODDIR=/boot/kernel 2>&1 | tail -20 test -s ng_ipfw.ko || { echo "[-] ng_ipfw.ko not built"; exit 1; } ls -l ng_ipfw.ko echo "[*] loading dependencies + ng_ipfw" kldload netgraph || true kldload ipfw || true kldload "$WRK/ng_ipfw.ko" kldstat | grep -E "ng_ipfw|ipfw|netgraph" echo "[*] verifying the ipfw ng node exists" ngctl list 2>/dev/null | grep -E "Name:|Type:" | head -20 ngctl show ipfw: 2>&1 || true echo "[*] installing ipfw ngtee rule (rule 1) -- both in/out" # ngtee <rule> delivers a COPY of the packet to the netgraph ipfw node's # hook named after the rule number; the original keeps flowing. The dir # passed to ng_ipfw_input is IP_FW_DIVERT_DIR (in for inbound, out for # outbound). We add both so the first packet that arrives triggers it. ipfw -q flush 2>/dev/null || true ipfw -q add 100 ngtee 1 ip from any to any in via vtnet0 ipfw -q add 110 ngtee 1 ip from any to any out via vtnet0 ipfw list 2>&1 | head echo "[*] sending one ICMP packet (self ping) -- expected to panic the kernel" sync # Give the kernel a moment, then ping. The packet traverses ipfw -> ngtee -> # ng_ipfw_input -> NG_SEND_DATA_ONLY -> ng worker thread -> ng_ipfw_rcvdata # -> ip_input(m) directly -> ASSERT_NETISR_NCPUS fires -> panic. ping -c 1 -t 2 10.0.2.2 >/dev/null 2>&1 || true ping -c 1 -t 2 127.0.0.1 >/dev/null 2>&1 || true echo "[!] if the kernel is still up after this, the assertion did NOT fire." echo " check the serial boot log / dmesg for ASSERT_NETISR_NCPUS." exit 0 |