⬢ DragonFlyBSD Kernel Audit
DF-5518 / gpo_race3.c
← back to finding ↓ download raw
/*
 * gpo_race3.c -- DF-5518 race trigger, fix-validation variant.
 *
 * Same defect demonstration as gpo_race2.c, but detach runs on its own
 * kernel thread and the provider lock is released 500ms later (well past
 * the ~250ms detach duration), so:
 *
 *   stock kernel:  detach frees softc/device_t/resources and returns;
 *                  the delayed LK_RELEASE wakes the blocked in-flight
 *                  gpio_acpi_space_handler which then dereferences the
 *                  freed objects -> Fatal trap (gpio_cherryview_read_pin).
 *
 *   fixed kernel:  gpio_acpi_detach sets sc->detaching, removes the space
 *                  handler and DRAINS in-flight dispatches; the detach
 *                  thread sleeps in the drain until this module releases
 *                  the provider lock; the blocked handler then completes
 *                  against still-valid state and detach returns cleanly
 *                  (no panic, guest stays up).
 */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/kthread.h>
#include <machine/cpufunc.h>
#include <vm/vm_page.h>

struct gpio_intel_softc_hack {
	device_t dev;
	struct resource *mem_res;
	struct resource *irq_res;
	void *intrhand;
	struct lock lk;
};

static device_t race_dev;
static int race_error;

static volatile uint32_t *dbgmark = (volatile uint32_t *)0x1;

static void
detach_thread(void *arg)
{
	kprintf("gpo_race3: detaching with in-flight handler...\n");
	*dbgmark = 0x600D0001;
	race_error = device_detach(race_dev);
	*dbgmark = 0x600D0000 + (uint32_t)(race_error & 0xff);
	kprintf("gpo_race3: detach returned %d\n", race_error);
}

static int
gpo_race3_modevent(module_t mod, int type, void *arg)
{
	devclass_t dc;
	struct gpio_intel_softc_hack *schk;

	switch (type) {
	case MOD_LOAD:
		dc = devclass_find("gpio_intel");
		if (dc == NULL) {
			kprintf("gpo_race3: no gpio_intel devclass\n");
			return (ENXIO);
		}
		race_dev = devclass_get_device(dc, 0);
		if (race_dev == NULL) {
			kprintf("gpo_race3: no gpio_intel0\n");
			return (ENXIO);
		}
		schk = device_get_softc(race_dev);
		dbgmark = (volatile uint32_t *)PHYS_TO_DMAP(0xD6008);
		*dbgmark = 0;

		kprintf("gpo_race3: holding gpio_intel0 sc->lk; next GPIO "
		    "dispatch blocks mid-handler\n");
		lockmgr(&schk->lk, LK_EXCLUSIVE);

		/* let the hammer dispatch into the blocked handler */
		tsleep(&race_error, 0, "gporace3", 100);

		kthread_create(detach_thread, NULL, NULL, "gpo_race3");
		/* detach thread: _REG(200ms) + teardown + (fixed: drain) */

		/* release after detach has passed the free phase */
		tsleep(&race_error, 0, "gporace3b", 500);
		lockmgr(&schk->lk, LK_RELEASE);
		kprintf("gpo_race3: lock released; any in-flight handler "
		    "has now resumed\n");
		break;
	case MOD_UNLOAD:
		break;
	default:
		return (EOPNOTSUPP);
	}
	return (0);
}

static moduledata_t gpo_race3_mod = {
	"gpo_race3", gpo_race3_modevent, NULL
};
DECLARE_MODULE(gpo_race3, gpo_race3_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(gpo_race3, 1);