/*
 * Variant battery for the sys_xsyscall double-fault crash.
 * usage: sysent_var <variant>
 *   b1 = indirect getpid    syscall(0, 20)              narg=0
 *   b2 = indirect write     syscall(0, 4, fd, buf, n)   narg=3 (<= regcnt)
 *   c  = indirect mmap      syscall(0, 197, ...) 7 args (narg=7 > regcnt=5)  [known crasher]
 *   d  = RAW asm indirect mmap: exact regs, stack args pad=0,pos=0 at rsp+8/rsp+16
 *   e  = RAW asm DIRECT mmap (rax=197): 6 regs + stack args at rsp+8 (pad), rsp+16 (pos)
 *   f  = indirect via __syscall(198, ...) mmap (libc wrapper path)
 *   g  = RAW asm indirect with stack args beyond mapped user stack (rsp+8 unmapped)
 */
#include <sys/syscall.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

/* raw indirect syscall 0: rdi=code, rsi..r9=a1..a5, [rsp+8]=a6, [rsp+16]=a7 */
static long raw_ind(long code, long a1, long a2, long a3, long a4, long a5,
		    long a6, long a7)
{
	long ret;
	__asm__ volatile(
	    "subq $32, %%rsp\n\t"
	    "movq %[a6], 8(%%rsp)\n\t"
	    "movq %[a7], 16(%%rsp)\n\t"
	    "movq $0, (%%rsp)\n\t"          /* slot kernel skips (+8 offset) */
	    "movl $0, %%eax\n\t"
	    "movq %[code], %%rdi\n\t"
	    "movq %[a1], %%rsi\n\t"
	    "movq %[a2], %%rdx\n\t"
	    "movq %[a3], %%rcx\n\t"
	    "movq %[a4], %%r8\n\t"
	    "movq %[a5], %%r9\n\t"
	    "syscall\n\t"
	    "addq $32, %%rsp\n\t"
	    : "=a"(ret)
	    : [code]"r"(code), [a1]"r"(a1), [a2]"r"(a2), [a3]"r"(a3),
	      [a4]"r"(a4), [a5]"r"(a5), [a6]"r"(a6), [a7]"r"(a7)
	    : "rcx", "r11", "memory");
	return ret;
}

/* raw direct syscall: rax=n, rdi..r9=a1..a6, [rsp+8]=a7, [rsp+16]=a8 */
static long raw_dir(long n, long a1, long a2, long a3, long a4, long a5,
		    long a6, long a7, long a8)
{
	long ret;
	__asm__ volatile(
	    "subq $32, %%rsp\n\t"
	    "movq %[a7], 8(%%rsp)\n\t"
	    "movq %[a8], 16(%%rsp)\n\t"
	    "movq $0, (%%rsp)\n\t"
	    "movq %[n], %%rax\n\t"
	    "movq %[a1], %%rdi\n\t"
	    "movq %[a2], %%rsi\n\t"
	    "movq %[a3], %%rdx\n\t"
	    "movq %[a4], %%rcx\n\t"
	    "movq %[a5], %%r8\n\t"
	    "movq %[a6], %%r9\n\t"
	    "syscall\n\t"
	    "addq $32, %%rsp\n\t"
	    : "=a"(ret)
	    : [n]"r"(n), [a1]"r"(a1), [a2]"r"(a2), [a3]"r"(a3), [a4]"r"(a4),
	      [a5]"r"(a5), [a6]"r"(a6), [a7]"r"(a7), [a8]"r"(a8)
	    : "rcx", "r11", "memory");
	return ret;
}

int main(int argc, char **argv)
{
	char v = argc > 1 ? argv[1][0] : 'z';
	long r;
	const char msg[] = "X\n";

	switch (v) {
	case 'p':
		r = syscall(SYS_syscall, 20);
		printf("[p] indirect getpid = %ld\n", r);
		break;
	case 'w':
		r = syscall(SYS_syscall, 4, 1, msg, 2);
		printf("[w] indirect write = %ld\n", r);
		break;
	case 'c':
		printf("[c] indirect mmap before\n"); fflush(stdout);
		r = syscall(SYS_syscall, 197, 0, 4096, PROT_READ|PROT_WRITE,
			    MAP_ANON|MAP_PRIVATE, -1, 0);
		printf("[c] indirect mmap = 0x%lx\n", (unsigned long)r);
		break;
	case 'd':
		printf("[d] raw indirect mmap before\n"); fflush(stdout);
		r = raw_ind(197, 0, 4096, PROT_READ|PROT_WRITE,
			    MAP_ANON|MAP_PRIVATE, -1, 0, 0);
		printf("[d] raw indirect mmap = 0x%lx\n", (unsigned long)r);
		break;
	case 'e':
		printf("[e] raw direct mmap before\n"); fflush(stdout);
		r = raw_dir(197, 0, 4096, PROT_READ|PROT_WRITE,
			    MAP_ANON|MAP_PRIVATE, -1, 0, 0, 0);
		printf("[e] raw direct mmap = 0x%lx\n", (unsigned long)r);
		break;
	case 'f':
		printf("[f] indirect __syscall mmap before\n"); fflush(stdout);
		r = syscall(SYS___syscall, 197, 0, 4096, PROT_READ|PROT_WRITE,
			    MAP_ANON|MAP_PRIVATE, -1, 0);
		printf("[f] __syscall mmap = 0x%lx\n", (unsigned long)r);
		break;
	case 'g':
		printf("[g] raw indirect, stack args unmapped\n"); fflush(stdout);
		r = raw_ind(197, 0, 4096, PROT_READ|PROT_WRITE,
			    MAP_ANON|MAP_PRIVATE, -1, 0, 0);
		printf("[g] ret = 0x%lx\n", (unsigned long)r);
		break;
	default:
		printf("unknown variant\n");
		return 2;
	}
	return 0;
}
