#include <linux/userfaultfd.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int userfaultfd(int flags)
{
	return syscall(__NR_userfaultfd, flags);
}

// Only exists since v4.10, so not in most distros...
#ifndef UFFD_FEATURE_EVENT_FORK
#define UFFD_FEATURE_EVENT_FORK 2
#endif

// Arbitrary; needs to be some PAGE_SIZE multiple
#define REGION_SIZE	(2 * 1024 * 1024)

int uffd_setup(void *base, size_t size)
{
	int uffd;
	struct uffdio_api ufa = {
		.api = UFFD_API,
		.features = UFFD_FEATURE_EVENT_FORK,
		.ioctls = 0,
	};
	struct uffdio_register ufr = {
		.range.start = (unsigned long)base,
		.range.len = size,
		.mode = UFFDIO_REGISTER_MODE_MISSING,
		.ioctls = 0,
	};
	
	uffd = userfaultfd(0);
	ioctl(uffd, UFFDIO_API, &ufa);
	ioctl(uffd, UFFDIO_REGISTER, &ufr);

	return uffd;
}

void *thr_uffd(void *unused)
{
	void *base;
	int uffd;
	struct uffd_msg msg;
	struct rlimit rlimit;

	base = mmap(NULL, REGION_SIZE, PROT_READ | PROT_WRITE,
		    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

	uffd = uffd_setup(base, REGION_SIZE);

	rlimit.rlim_cur = 0;
	rlimit.rlim_max = 0;
	setrlimit(RLIMIT_NOFILE, &rlimit);

	read(uffd, &msg, sizeof(msg));

	return NULL;
}

void *thr_clone(void *unused)
{
	fork();
	return NULL;
}

int main(int argc, char *argv[])
{
	pthread_t p_uffd, p_clone;

	pthread_create(&p_uffd, 0, thr_uffd, NULL);
	usleep(1000);
	pthread_create(&p_clone, 0, thr_clone, NULL);
	usleep(1000);

	return 0;
}