⬢ DragonFlyBSD Kernel Audit
DF-4996 / df4996.c
← back to finding ↓ download raw
/*
 * DF-4996 / DF-4997 mechanism prover (DragonFly 6.5-DEVELOPMENT x86_64).
 *
 * DF-4996: ral_pci_attach() calls ral_pci_detach() when the child attach
 *	    (rt2560/rt2661/rt2860) fails.  The child has already unwound its
 *	    own state at that point and never called ieee80211_ifattach().
 *	    The second-pass child detach then reaches
 *	    ieee80211_ifdetach() (netproto/802_11/wlan/ieee80211.c:410) whose
 *	    LIST_REMOVE(ic, ic_next) writes through the zeroed le_prev ==
 *	    NULL: kernel NULL-pointer write -> fatal trap 12 panic.
 *	    This module reproduces the exact sink on the audit QEMU guest
 *	    (which has no Ralink PCI hardware): a freshly-zeroed ieee80211com
 *	    is exactly the state the ral softc is in on that path (newbus
 *	    zeroes device softcs at allocation).
 *
 * DF-4997: if_ral_pci.c writes sc_invalid through the rt2560 union alias.
 *	    rt2860var.h drifted from the "same in both up to here" prefix
 *	    invariant (sc_snd moved before sc_mtx, sc_tx_timer dropped from
 *	    the prefix), so for rt2860-family devices the write lands on
 *	    rt2860_softc.sc_debug and rt2860_softc.sc_invalid stays 0.
 *	    This module prints the true compiled offsets.
 *
 * MOD_LOAD is safe (offsets only).  The DF-4996 sink is triggered by
 * writing to the kern.df4996 sysctl, so the offset evidence survives even
 * if the panic capture is botched.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/bus.h>

#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/route.h>

#include <netproto/802_11/ieee80211_var.h>

#include <dev/netif/ral/rt2560var.h>
#include <dev/netif/ral/rt2661var.h>
#include <dev/netif/ral/rt2860var.h>

#define	OFF(s, f)	__offsetof(s, f)

static void
df4997_report_offsets(void)
{
	kprintf("DF4997: sizeof(struct lock)=%zu sizeof(struct mbufq)=%zu\n",
	    sizeof(struct lock), sizeof(struct mbufq));

	kprintf("DF4997: rt2560_softc size=%zu: sc_st=%zu sc_sh=%zu "
	    "watchdog_ch=%zu sc_tx_timer=%zu sc_invalid=%zu sc_debug=%zu\n",
	    sizeof(struct rt2560_softc),
	    OFF(struct rt2560_softc, sc_st),
	    OFF(struct rt2560_softc, sc_sh),
	    OFF(struct rt2560_softc, watchdog_ch),
	    OFF(struct rt2560_softc, sc_tx_timer),
	    OFF(struct rt2560_softc, sc_invalid),
	    OFF(struct rt2560_softc, sc_debug));

	kprintf("DF4997: rt2661_softc size=%zu: sc_invalid=%zu sc_debug=%zu\n",
	    sizeof(struct rt2661_softc),
	    OFF(struct rt2661_softc, sc_invalid),
	    OFF(struct rt2661_softc, sc_debug));

	kprintf("DF4997: rt2860_softc size=%zu: sc_st=%zu sc_sh=%zu "
	    "watchdog_ch=%zu sc_invalid=%zu sc_debug=%zu sc_tx_timer=%zu\n",
	    sizeof(struct rt2860_softc),
	    OFF(struct rt2860_softc, sc_st),
	    OFF(struct rt2860_softc, sc_sh),
	    OFF(struct rt2860_softc, watchdog_ch),
	    OFF(struct rt2860_softc, sc_invalid),
	    OFF(struct rt2860_softc, sc_debug),
	    OFF(struct rt2860_softc, sc_tx_timer));

	if (OFF(struct rt2560_softc, sc_invalid) ==
	    OFF(struct rt2860_softc, sc_invalid))
		kprintf("DF4997: VERDICT prefixes agree (claim FALSE)\n");
	else if (OFF(struct rt2560_softc, sc_invalid) ==
	    OFF(struct rt2860_softc, sc_debug))
		kprintf("DF4997: VERDICT rt2560.sc_invalid alias == "
		    "rt2860.sc_debug: glue writes MISS sc_invalid and hit "
		    "sc_debug on rt2860 (claim TRUE)\n");
	else
		kprintf("DF4997: VERDICT misaligned onto some other field "
		    "(delta %zd)\n",
		    (ssize_t)OFF(struct rt2560_softc, sc_invalid) -
		    (ssize_t)OFF(struct rt2860_softc, sc_invalid));

	if (OFF(struct rt2560_softc, sc_st) == OFF(struct rt2860_softc, sc_st) &&
	    OFF(struct rt2560_softc, sc_sh) == OFF(struct rt2860_softc, sc_sh))
		kprintf("DF4997: sc_st/sc_sh offsets DO coincide (bus tag/"
		    "handle writes land correctly - impact limited to "
		    "sc_invalid/sc_debug)\n");
	else
		kprintf("DF4997: sc_st/sc_sh offsets ALSO diverge (worse than "
		    "claimed!)\n");
}

static void
df4996_trigger_sink(void)
{
	struct ieee80211com *ic;

	/* newbus softc state after failed child attach: all-zero ic */
	ic = kmalloc(sizeof(*ic), M_DEVBUF, M_WAITOK | M_ZERO);
	kprintf("DF4996: zeroed ic=%p (== ral child softc after failed attach:"
	    " ieee80211_ifattach() never ran)\n", ic);
	kprintf("DF4996: invoking ieee80211_ifdetach(ic) ...\n");

	ieee80211_ifdetach(ic);

	kprintf("DF4996: SURVIVED ieee80211_ifdetach - mechanism NOT "
	    "reproduced (unexpected)\n");
	kfree(ic, M_DEVBUF);
}

static int
df4996_sysctl(SYSCTL_HANDLER_ARGS)
{
	char buf[16];
	int error;

	memset(buf, 0, sizeof(buf));
	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	kprintf("DF4996: sysctl trigger '%s'\n", buf);
	df4996_trigger_sink();

	return (0);
}
SYSCTL_PROC(_kern, OID_AUTO, df4996, CTLTYPE_STRING | CTLFLAG_RW,
    0, 0, df4996_sysctl, "A",
    "write any string to trigger the DF-4996 ifdetach sink demo");

static int
df4996_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("DF4996/DF4997 mechanism prover loaded\n");
		df4997_report_offsets();
		return (0);
	case MOD_UNLOAD:
		return (0);
	default:
		return (0);
	}
}

static moduledata_t df4996_mod = {
	"df4996",
	df4996_modevent,
	NULL
};
DECLARE_MODULE(df4996, df4996_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);