⬢ DragonFlyBSD Kernel Audit
DF-4528 / nvmebang.c
← back to finding ↓ download raw
/*
 * nvmebang.c -- DF-4528/DF-4529 PoC trigger v2 (TEST ONLY, never for sys/).
 *
 * Bypasses newbus state bookkeeping and invokes the nvme driver's OWN
 * attach/detach driver-methods on unit 0, so the device_t remains in the
 * devclass and the operation can be repeated indefinitely:
 *
 *   sysctl hw.nvmebang.action=1   -> run nvme_pci_detach() (teardown race)
 *   sysctl hw.nvmebang.action=2   -> run nvme_pci_attach() (identify storm)
 *
 * This reproduces, on demand and repeatably, what a PCI hot-unplug /
 * surprise removal (action=1) and hot-plug / rescan (action=2) do to the
 * driver while user processes hold namespace devices open and are issuing
 * NVMEIOCGETLOG admin ioctls.
 */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/sysctl.h>
#include <sys/kobj.h>

static int nvmebang_action(SYSCTL_HANDLER_ARGS);

static int nvmebang_act = 0;
SYSCTL_NODE(_hw, OID_AUTO, nvmebang, CTLFLAG_RW, 0, "nvme bang test");
SYSCTL_PROC(_hw_nvmebang, OID_AUTO, action, CTLTYPE_INT | CTLFLAG_RW,
    &nvmebang_act, 0, nvmebang_action, "I", "1=detach 2=attach");

static int
nvmebang_action(SYSCTL_HANDLER_ARGS)
{
	devclass_t dc;
	driver_t *drv;
	kobj_method_t *m;
	device_t dev;
	int error;
	int (*attach_fn)(device_t) = NULL;
	int (*detach_fn)(device_t) = NULL;

	error = sysctl_handle_int(oidp, &nvmebang_act, 0, req);
	if (error || req->newptr == NULL)
		return (error);
	if (nvmebang_act == 0)
		return (0);

	dc = devclass_find("nvme");
	if (dc == NULL) {
		kprintf("nvmebang: devclass 'nvme' not found\n");
		return (ENOENT);
	}
	dev = devclass_get_device(dc, 0);
	if (dev == NULL) {
		kprintf("nvmebang: nvme0 not present\n");
		return (ENOENT);
	}
	drv = device_get_driver(dev);
	if (drv == NULL || drv->methods == NULL) {
		kprintf("nvmebang: no driver\n");
		return (ENOENT);
	}
	for (m = drv->methods; m && m->func; ++m) {
		if (m->desc == NULL)
			continue;
		if (m->desc == &device_attach_desc)
			attach_fn = (int (*)(device_t))m->func;
		else if (m->desc == &device_detach_desc)
			detach_fn = (int (*)(device_t))m->func;
	}
	kprintf("nvmebang: methods attach=%p detach=%p\n",
	    attach_fn, detach_fn);

	switch (nvmebang_act) {
	case 1:
		if (detach_fn == NULL)
			return (ENOENT);
		kprintf("nvmebang: DETACH method on %s%d...\n",
		    device_get_name(dev), device_get_unit(dev));
		error = detach_fn(dev);
		kprintf("nvmebang: DETACH method done error=%d\n", error);
		break;
	case 2:
		if (attach_fn == NULL)
			return (ENOENT);
		kprintf("nvmebang: ATTACH method on %s%d...\n",
		    device_get_name(dev), device_get_unit(dev));
		error = attach_fn(dev);
		kprintf("nvmebang: ATTACH method done error=%d\n", error);
		break;
	default:
		return (EINVAL);
	}
	nvmebang_act = 0;
	return (0);
}

static int
nvmebang_load(module_t mod, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("nvmebang: loaded (sysctl hw.nvmebang.action)\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	default:
		return (0);
	}
}

static moduledata_t nvmebang_mod = { "nvmebang", nvmebang_load, NULL };
DECLARE_MODULE(nvmebang, nvmebang_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(nvmebang, 1);