#!/bin/sh
# DF-4590 trigger — tws(4) driver-callout use-after-free on detach/unload
#
# VULNERABILITY: sys/dev/raid/tws/tws.c:346-410 (tws_detach) and the
# attach-failure unwinds (tws.c:309-341) never cancel the driver's
# callouts before freeing the memory they reference:
#
#   sc->reset_cb_handle   scheduled tws_cam.c:1217,1232 (hz/10 poll loop)
#   sc->reinit_handle     scheduled tws_cam.c:1238,1256 (5 s poll loop,
#                         self-reschedules while !tws_ctlr_ready())
#   req->thandle          scheduled tws_cam.c:847,893 (60 s timeouts on
#                         AEN-fetch / get-set-param requests)
#
# tws_detach() tears down the interrupt, kfrees sc->reqs (tws.c:398),
# kfree()s the queues, and lockuninit()s — with no callout_stop/cancel
# anywhere — so a pending callout fires into freed memory (UAF panic or
# heap corruption in softirq context).
#
# TRIGGER RECIPE (requires a 3ware 9700-series controller, PCI 13C1:1010):
#   1. Build tws as a module:  cd sys/dev/raid/tws && make && kldload ./tws.ko
#      (X86_64_GENERIC compiles tws into the kernel; module config is needed
#      so the code can be unloaded — see VERDICT.md preconditions.)
#   2. Induce controller distress so a reset/reinit poll is pending, e.g.:
#        - cause I/O errors / pull the controller into tws_reset (watch
#          dmesg for "Resetting controller" — this schedules
#          reset_cb_handle at hz/10 and, when the soft reset does not
#          complete, reinit_handle every 5 s indefinitely), or
#        - leave an AEN fetch / param op in flight (its 60 s thandle).
#   3. While the reinit poll is cycling (dmesg keeps printing nothing new
#      but the callout keeps rescheduling), as root:
#          kldunload tws
#   4. EXPECTED (vulnerable): within <= 5 s the softclock handler calls
#      tws_reinit()/tws_reset_cb()/tws_timeout() on freed memory ->
#      kernel panic (page fault in softirq / slab corruption), or callout
#      jumps into unmapped module text.
#      PATCHED (fix.diff): kldunload returns cleanly; no panic.
#
# On the audit QEMU guest there is no 13C1:1010 controller, so this script
# only records the hardware absence (see run.log).
set -e
echo "== guest =="
uname -a
echo "== LSI 3ware (0x13C1) controller present? =="
if pciconf -l 2>/dev/null | grep -q "13c1"; then
    echo "3ware controller present — run the recipe above as root."
else
    echo "NO 3ware controller (vendor 0x13C1) on this guest — hardware-gated."
    kldload tws 2>&1 || true
    sysctl dev.tws 2>&1 | head -1 || true
fi
