/*
 * Kernel-stack leak detector: N raw indirect syscalls in a loop.
 * usage: leaktest <mode> [count]
 *   mode i = indirect getpid   (rax=0, rdi=20)          narg=0
 *   mode w = indirect write    (rax=0, rdi=4,rsi,rdx)   narg=3
 *   mode m = indirect mmap     (rax=0, rdi=197, 7 args) narg=7 (copyin path)
 *   mode d = DIRECT mmap       (rax=197, 7 args)        control
 *   mode s = direct getpid     (rax=20)                 control
 * Prints progress markers via raw write so libc never touches the path.
 * Exit code: 0 = survived all iterations.
 */
#include <stdio.h>
#include <stdlib.h>

#define PROD 0

static long raw_ind0(long code)
{
	long ret;
	__asm__ volatile(
	    "movl $0, %%eax\n\t"
	    "movq %[c], %%rdi\n\t"
	    "syscall"
	    : "=a"(ret) : [c]"r"(code) : "rcx", "r11", "memory");
	return ret;
}

static long raw_ind3(long code, long a1, long a2, long a3)
{
	long ret;
	__asm__ volatile(
	    "movl $0, %%eax\n\t"
	    "movq %[c], %%rdi\n\t"
	    "movq %[a1], %%rsi\n\t"
	    "movq %[a2], %%rdx\n\t"
	    "movq %[a3], %%rcx\n\t"
	    "syscall"
	    : "=a"(ret) : [c]"r"(code), [a1]"r"(a1), [a2]"r"(a2), [a3]"r"(a3)
	    : "rcx", "r11", "memory");
	return ret;
}

static long raw_ind7(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"
	    "movl $0, %%eax\n\t"
	    "movq %[c], %%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"
	    : "=a"(ret)
	    : [c]"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;
}

static long raw_dir7(long n, 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"
	    "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"
	    : "=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)
	    : "rcx", "r11", "memory");
	return ret;
}

static long raw_getpid(void)
{
	long ret;
	__asm__ volatile("movl $20, %%eax\n\tsyscall" : "=a"(ret) :: "rcx","r11","memory");
	return ret;
}

static void raw_write(const char *s, int len)
{
	__asm__ volatile(
	    "movl $4, %%eax\n\t"
	    "movl $1, %%edi\n\t"
	    "syscall"
	    :: "D"(1), "S"(s), "d"(len) : "rax","rcx","r11","memory");
}

int main(int argc, char **argv)
{
	char mode = argc > 1 ? argv[1][0] : 'i';
	long n = argc > 2 ? atol(argv[2]) : 5000;
	char buf[64];
	long i, r = 0;

	for (i = 0; i < n; i++) {
		switch (mode) {
		case 'i': r = raw_ind0(20); break;                    /* getpid */
		case 'w': r = raw_ind3(4, 1, "", 0); break;           /* write fd1, 0 bytes */
		case 'm': r = raw_ind7(197, 0, 4096, 3, 0x1002, -1, 0, 0); break;
		case 'd': r = raw_dir7(197, 0, 4096, 3, 0x1002, -1, 0, 0); break;
		case 's': r = raw_getpid(); break;
		}
		if ((i % 64) == 0) {
			int l = snprintf(buf, sizeof(buf), "%c %ld\n", mode, i);
			raw_write(buf, l);
		}
		if (mode == 'm' || mode == 'd') {
			/* munmap the result to avoid exhausting the map space */
			__asm__ volatile(
			    "movl $73, %%eax\n\t"      /* SYS_munmap */
			    "syscall"
			    :: "D"(r), "S"(4096L) : "rax","rcx","r11","memory");
		}
	}
	printf("%c survived %ld iterations (last=%ld)\n", mode, n, r);
	return 0;
}
