/*
 * Stage-driven bisect of the sysent[] edge-case crash.
 * usage: sysent_stage <stage>
 *   1 = OOR syscall 556 (in child)
 *   2 = OOR 0x7fffffff (in child)
 *   3 = lkm slot 210 (in child)
 *   4 = direct 7-arg mmap
 *   5 = indirect mmap syscall(0, 197, ...) 8 total args
 *   6 = indirect mmap __syscall(198, 197, ...)
 *   7 = lseek 4GiB (rsize=8)
 *   8 = getrandom (rsize=8)
 */
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

static long sc(long n, long a, long b, long c, long d, long e, long f, long g)
{
	return syscall(n, a, b, c, d, e, f, g);
}

int main(int argc, char **argv)
{
	int stage = argc > 1 ? atoi(argv[1]) : 0;
	pid_t p;
	int st;

	switch (stage) {
	case 1:
		p = fork();
		if (p == 0) { sc(556,0,0,0,0,0,0,0); _exit(1); }
		waitpid(p, &st, 0);
		printf("[1] 556 -> %s\n", WIFSIGNALED(st) ? "signaled" : "exit");
		break;
	case 2:
		p = fork();
		if (p == 0) { sc(0x7fffffff,0,0,0,0,0,0,0); _exit(1); }
		waitpid(p, &st, 0);
		printf("[2] INT_MAX -> %s\n", WIFSIGNALED(st) ? "signaled" : "exit");
		break;
	case 3:
		p = fork();
		if (p == 0) { sc(210,0,0,0,0,0,0,0); _exit(1); }
		waitpid(p, &st, 0);
		printf("[3] lkm 210 -> %s\n", WIFSIGNALED(st) ? "signaled" : "exit");
		break;
	case 4: {
		void *m = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
		printf("[4] direct mmap = %p\n", m);
		break;
	}
	case 5: {
		long im = sc(SYS_syscall, 197, 0, 4096, PROT_READ|PROT_WRITE,
			     MAP_ANON|MAP_PRIVATE, -1, 0);
		printf("[5] indirect mmap(syscall) = 0x%lx\n", (unsigned long)im);
		break;
	}
	case 6: {
		long im = sc(SYS___syscall, 197, 0, 4096, PROT_READ|PROT_WRITE,
			     MAP_ANON|MAP_PRIVATE, -1, 0);
		printf("[6] indirect mmap(__syscall) = 0x%lx\n", (unsigned long)im);
		break;
	}
	case 7: {
		int fd = open("/tmp/stage.tmp", O_RDWR|O_CREAT, 0600);
		off_t r = lseek(fd, 0x100000000LL, SEEK_SET);
		printf("[7] lseek = %lld\n", (long long)r);
		close(fd); unlink("/tmp/stage.tmp");
		break;
	}
	case 8: {
		unsigned char rb[8];
		long gr = syscall(SYS_getrandom, rb, 8, 0);
		printf("[8] getrandom = %ld\n", gr);
		break;
	}
	default:
		printf("unknown stage\n");
		return 2;
	}
	return 0;
}
