DragonFlyBSD Kernel Audit
DF-0759 / run.sh
← back to finding ↓ download raw
#!/bin/sh
# DF-0759 run.sh — reproduce the double NG_FREE_ITEM KKASSERT panic in ng_split.
#
# CONTEXT (read before judging the result):
#   * ng_split lives ONLY in sys/netgraph7/ (the opt-in parallel netgraph
#     stack). It is NOT in the default X86_64_GENERIC kernel and is NOT in the
#     default module build (Makefile.modules picks sys/netgraph unless
#     WANT_NETGRAPH7 is set; ng_split also has no upstream module Makefile).
#   * Reaching the out-hook data path needs a netgraph topology, which needs
#     the privileged netgraph control socket -> ROOT ONLY.
#   * The default X86_64_GENERIC kernel ships `options INVARIANTS`. When
#     ng_split is compiled with INVARIANTS (either compiled into such a kernel
#     via `options NETGRAPH7_SPLIT`, OR as a kld module built with
#     KCFLAGS=-DINVARIANTS), the KKASSERT inside NG_FREE_ITEM (netgraph.h:818)
#     is active and the double NG_FREE_ITEM PANICS. Without INVARIANTS the
#     second NG_FREE_ITEM is a harmless idempotent flag-set (KKASSERT no-op).
#   * NOTE on kld modules: bsd.kmod.mk does NOT inherit the kernel's INVARIANTS
#     define, so a stock kld build has KKASSERT compiled out. This script
#     therefore builds ng_split with KCFLAGS=-DINVARIANTS to reproduce the
#     INVARIANTS-kernel behaviour the finding describes.
#
# This script runs as ROOT and:
#   1. builds netgraph7 core + ng_socket7 (stock, no INVARIANTS) and ng_split
#      (WITH INVARIANTS so its NG_FREE_ITEM assertion is live),
#   2. loads the netgraph7 stack + ng_split,
#   3. drives a single data byte onto a split node's "out" hook via the
#      `inject` userland helper (linked against libnetgraph7, NG_VERSION=8).
#
# Expected (bug present, INVARIANTS active):
#   assertion "!(item->el_flags & NGQF_FREE)" failed in ng_split_rcvdata
#       at netgraph7/netgraph.h:818  ->  kernel panic, ssh dies.
# Expected (bug present, INVARIANTS OFF, or AFTER the fix):
#   "NO_PANIC" printed, guest stays up.

set -e
cd "$(dirname "$0")"

echo "=== [1/4] netgraph7 core module ==="
make -C /usr/src/sys/netgraph7/netgraph obj 2>&1 | tail -1
make -C /usr/src/sys/netgraph7/netgraph 2>&1 | tail -2
cp /usr/obj/usr/src/sys/netgraph7/netgraph/netgraph.ko /root/netgraph7.ko

echo "=== [2/4] ng_socket7 module ==="
make -C /usr/src/sys/netgraph7/socket obj 2>&1 | tail -1
make -C /usr/src/sys/netgraph7/socket 2>&1 | tail -2
cp /usr/obj/usr/src/sys/netgraph7/socket/ng_socket.ko /root/ng_socket7.ko

echo "=== [3/4] ng_split module (INVARIANTS active -> KKASSERT live) ==="
mkdir -p /usr/src/sys/netgraph7/split
cp /usr/src/sys/netgraph7/ng_split.c /usr/src/sys/netgraph7/split/
cd /usr/src/sys/netgraph7/split
ln -sf ../ng_message.h .
ln -sf ../netgraph.h .
ln -sf ../dragonfly.h .
ln -sf ../ng_split.h .
touch opt_netgraph.h
cat > Makefile <<'MK'
KMOD=		ng_split
SRCS=		ng_split.c
KCFLAGS=	-DINVARIANTS
.include <bsd.kmod.mk>
MK
make obj 2>&1 | tail -1
make 2>&1 | tail -2
cp /usr/obj/usr/src/sys/netgraph7/split/ng_split.ko /root/
# sanity: the assertion string must be present (KKASSERT compiled in)
strings /root/ng_split.ko | grep -q "NGQF_FREE" && \
	echo "[+] ng_split.ko has INVARIANTS assertion compiled in" || \
	{ echo "[-] ng_split.ko MISSING assertion -- INVARIANTS not active"; exit 1; }
cd -

echo "=== install libnetgraph7 (NG_VERSION=8) if not present ==="
if [ ! -e /usr/lib/libnetgraph.so.3 ]; then
	( cd /usr/src/lib/libnetgraph7 && make obj && make && make install ) 2>&1 | tail -3
fi

echo "=== [4/4] load netgraph7 stack + ng_split ==="
kldload /root/netgraph7.ko
kldload /root/ng_socket7.ko
kldload /root/ng_split.ko
kldstat | grep -iE "netgraph|ng_"

echo "=== compile injector (libnetgraph7 / NG_VERSION=8) ==="
cc -O2 -Wall -o inject inject.c -lnetgraph
ls -l inject

echo "=== FIRING TRIGGER: data byte -> split:out -> ng_split_rcvdata(out) ==="
echo "    (double NG_FREE_ITEM; KKASSERT(!(NGQF_FREE)) should fire -> panic)"
./inject
echo "INJECT_EXIT=$?"