Home | History | Annotate | Download | only in x86
      1 /*
      2  * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
      3  * Copyright (c) 2015 Andrew Lutomirski
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms and conditions of the GNU General Public License,
      7  * version 2, as published by the Free Software Foundation.
      8  *
      9  * This program is distributed in the hope it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  */
     14 
     15 #define _GNU_SOURCE
     16 
     17 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 #include <sys/signal.h>
     21 #include <sys/ucontext.h>
     22 #include <err.h>
     23 #include <setjmp.h>
     24 #include <errno.h>
     25 
     26 /* Our sigaltstack scratch space. */
     27 static unsigned char altstack_data[SIGSTKSZ];
     28 
     29 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
     30 		       int flags)
     31 {
     32 	struct sigaction sa;
     33 	memset(&sa, 0, sizeof(sa));
     34 	sa.sa_sigaction = handler;
     35 	sa.sa_flags = SA_SIGINFO | flags;
     36 	sigemptyset(&sa.sa_mask);
     37 	if (sigaction(sig, &sa, 0))
     38 		err(1, "sigaction");
     39 }
     40 
     41 static volatile sig_atomic_t sig_traps;
     42 static sigjmp_buf jmpbuf;
     43 
     44 static volatile sig_atomic_t n_errs;
     45 
     46 static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
     47 {
     48 	ucontext_t *ctx = (ucontext_t*)ctx_void;
     49 
     50 	if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) {
     51 		printf("[FAIL]\tAX had the wrong value: 0x%x\n",
     52 		       ctx->uc_mcontext.gregs[REG_EAX]);
     53 		n_errs++;
     54 	} else {
     55 		printf("[OK]\tSeems okay\n");
     56 	}
     57 
     58 	siglongjmp(jmpbuf, 1);
     59 }
     60 
     61 static void sigill(int sig, siginfo_t *info, void *ctx_void)
     62 {
     63 	printf("[SKIP]\tIllegal instruction\n");
     64 	siglongjmp(jmpbuf, 1);
     65 }
     66 
     67 int main()
     68 {
     69 	stack_t stack = {
     70 		.ss_sp = altstack_data,
     71 		.ss_size = SIGSTKSZ,
     72 	};
     73 	if (sigaltstack(&stack, NULL) != 0)
     74 		err(1, "sigaltstack");
     75 
     76 	sethandler(SIGSEGV, sigsegv, SA_ONSTACK);
     77 	sethandler(SIGILL, sigill, SA_ONSTACK);
     78 
     79 	/*
     80 	 * Exercise another nasty special case.  The 32-bit SYSCALL
     81 	 * and SYSENTER instructions (even in compat mode) each
     82 	 * clobber one register.  A Linux system call has a syscall
     83 	 * number and six arguments, and the user stack pointer
     84 	 * needs to live in some register on return.  That means
     85 	 * that we need eight registers, but SYSCALL and SYSENTER
     86 	 * only preserve seven registers.  As a result, one argument
     87 	 * ends up on the stack.  The stack is user memory, which
     88 	 * means that the kernel can fail to read it.
     89 	 *
     90 	 * The 32-bit fast system calls don't have a defined ABI:
     91 	 * we're supposed to invoke them through the vDSO.  So we'll
     92 	 * fudge it: we set all regs to invalid pointer values and
     93 	 * invoke the entry instruction.  The return will fail no
     94 	 * matter what, and we completely lose our program state,
     95 	 * but we can fix it up with a signal handler.
     96 	 */
     97 
     98 	printf("[RUN]\tSYSENTER with invalid state\n");
     99 	if (sigsetjmp(jmpbuf, 1) == 0) {
    100 		asm volatile (
    101 			"movl $-1, %%eax\n\t"
    102 			"movl $-1, %%ebx\n\t"
    103 			"movl $-1, %%ecx\n\t"
    104 			"movl $-1, %%edx\n\t"
    105 			"movl $-1, %%esi\n\t"
    106 			"movl $-1, %%edi\n\t"
    107 			"movl $-1, %%ebp\n\t"
    108 			"movl $-1, %%esp\n\t"
    109 			"sysenter"
    110 			: : : "memory", "flags");
    111 	}
    112 
    113 	printf("[RUN]\tSYSCALL with invalid state\n");
    114 	if (sigsetjmp(jmpbuf, 1) == 0) {
    115 		asm volatile (
    116 			"movl $-1, %%eax\n\t"
    117 			"movl $-1, %%ebx\n\t"
    118 			"movl $-1, %%ecx\n\t"
    119 			"movl $-1, %%edx\n\t"
    120 			"movl $-1, %%esi\n\t"
    121 			"movl $-1, %%edi\n\t"
    122 			"movl $-1, %%ebp\n\t"
    123 			"movl $-1, %%esp\n\t"
    124 			"syscall\n\t"
    125 			"pushl $0"	/* make sure we segfault cleanly */
    126 			: : : "memory", "flags");
    127 	}
    128 
    129 	return 0;
    130 }
    131