โฌข DragonFlyBSD Kernel Audit
DF-3530 / harness_mod.c
โ† back to finding โ†“ download raw
/*
 * DF-3530 harness module โ€” deterministically confirms the ng_ether
 * freed-priv / NULL-private primitive.
 *
 * ng_ether_detach (sys/netgraph/ether/ng_ether.c:321-337) frees `priv`
 * and then sets node->private = NULL with no synchronization against the
 * data-plane readers (ng_ether_input :206-213, ng_ether_input_orphan
 * :222-230, ng_ether_output :260-273, ng_ether_rcvdata :505-513,
 * ng_ether_rcvmsg :401). A reader observing the post-teardown state on a
 * still-live interface dereferences private==NULL (or, in the (7)->(8)
 * sliver between kfree(priv) and node->private=NULL, freed memory).
 *
 * This module puts vtnet0's ng_ether node into the exact post-detach
 * observable state โ€” node stays valid (as an in-flight reader would have
 * loaded it before step (4)), node->private := NULL (as it is after (8)) โ€”
 * so the next packet on vtnet0 exercises the unguarded deref:
 *
 *   ping -> ether_output(vtnet0) -> ng_ether_output:
 *       const node_p node = IFP2NG(ifp);   // non-NULL
 *       const priv_p priv = node->private;  // NULL  (post-teardown state)
 *       if (priv->upper == NULL)            // FAULT at VA 0x8
 *
 * Fault VA 0x8 (offset of `upper` in struct private) distinguishes this
 * from DF-2587 (fault VA 0x28 = offset of `private` in struct ng_node,
 * node==NULL).
 *
 * On a FIXED ng_ether (priv==NULL guards) the same setup does NOT crash:
 * ng_ether_output returns 0 and the packet flows.
 */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_arp.h>
#include <netgraph/netgraph.h>		/* struct ng_node */

static struct ifnet *saved_ifp;
static void *saved_private;
static node_p saved_node;

static int
harness_load(module_t mod, int what, void *arg)
{
	struct arpcom *ac;
	struct ifnet *ifp;
	node_p node;

	switch (what) {
	case MOD_LOAD:
		ifnet_lock();			/* ifunit() requires ifnet_mtx */
		ifp = ifunit("vtnet0");
		if (ifp == NULL) {
			ifnet_unlock();
			kprintf("DF3530: vtnet0 not found\n");
			return (ENXIO);
		}
		ac = (struct arpcom *)ifp;	/* ac_if is first member of arpcom */
		node = ac->ac_netgraph;		/* IFP2NG(vtnet0) */
		if (node == NULL) {
			ifnet_unlock();
			kprintf("DF3530: no ng_ether node on vtnet0 (kldload ng_ether first)\n");
			return (ENXIO);
		}
		if (node->private == NULL) {
			ifnet_unlock();
			kprintf("DF3530: node->private already NULL\n");
			return (ENXIO);
		}
		saved_ifp = ifp;
		saved_node = node;
		saved_private = node->private;
		node->private = NULL;		/* post-teardown observable state */
		ifnet_unlock();
		kprintf("DF3530: vtnet0 ng_ether node=%p private=%p -> NULL. "
		    "Next packet -> ng_ether_output/input priv deref.\n",
		    saved_node, saved_private);
		return (0);
	case MOD_UNLOAD:
		ifnet_lock();
		if (saved_node != NULL) {
			saved_node->private = saved_private;
			kprintf("DF3530: restored node->private=%p\n", saved_private);
		}
		ifnet_unlock();
		return (0);
	}
	return (0);
}

static moduledata_t df3530_harness = {
	"df3530_harness",
	harness_load,
	NULL
};
DECLARE_MODULE(df3530_harness, df3530_harness, SI_SUB_PSEUDO, SI_ORDER_ANY);