/*
 * iretbug.c - probe the doreti_iret fault state machine
 * (trap.c:679-682 redirect -> ipl.s doreti_iret_fault frame rebuild)
 *
 * A signal handler modifies the saved user context so that the iretq
 * at doreti_iret faults when the kernel returns to user mode after
 * sigreturn.  The kernel must reconstruct a trap frame and deliver
 * SIGBUS/SIGSEGV; if the reconstruction is broken we expect a kernel
 * panic instead.
 *
 * variants:
 *   0: mc_ss = 0                 (null stack selector -> #GP at iretq)
 *   1: mc_ss = 0xdead            (garbage selector beyond GDT -> #GP)
 *   2: mc_rsp = 0x0000800000000000 (non-canonical rsp -> #GP)
 *   3: mc_cs = 0x2b              (data selector as cs; RPL3 so passes
 *                                  CS_SECURE check in sys_sigreturn)
 *   4: mc_ss = 0x1b|...          (code sel as ss)
 *   5: mc_ss = 0x4               (rpl0 selector as ss -> #GP)
 *   7: mc_ss = 0x10              (kernel data selector as ss)
 *   6: control - no modification (should exit cleanly)
 */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int variant = 6;

static void handler(int sig, siginfo_t *si, void *ctx)
{
	ucontext_t *uc = ctx;

	switch (variant) {
	case 0: uc->uc_mcontext.mc_ss = 0; break;
	case 1: uc->uc_mcontext.mc_ss = 0xdead; break;
	case 2: uc->uc_mcontext.mc_rsp = 0x0000800000000000LL; break;
	case 3: uc->uc_mcontext.mc_cs = 0x2b; break;
	case 4: uc->uc_mcontext.mc_ss = 0x1b; break;
	case 5: uc->uc_mcontext.mc_ss = 0x4; break;
	case 7: uc->uc_mcontext.mc_ss = 0x10; break;	/* kernel data sel */
	default: break;
	}
	printf("handler: variant %d, returning into modified frame\n", variant);
	fflush(stdout);
}

int main(int argc, char **argv)
{
	struct sigaction sa;

	if (argc > 1)
		variant = atoi(argv[1]);
	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = handler;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGUSR1, &sa, NULL);
	kill(getpid(), SIGUSR1);
	printf("SURVIVED: returned to unmodified context\n");
	return (0);
}
