linux/tools/testing/selftests/x86/syscall_arg_fault.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
   4 * Copyright (c) 2015 Andrew Lutomirski
   5 */
   6
   7#define _GNU_SOURCE
   8
   9#include <stdlib.h>
  10#include <stdio.h>
  11#include <string.h>
  12#include <sys/signal.h>
  13#include <sys/ucontext.h>
  14#include <err.h>
  15#include <setjmp.h>
  16#include <errno.h>
  17
  18#include "helpers.h"
  19
  20/* Our sigaltstack scratch space. */
  21static unsigned char altstack_data[SIGSTKSZ];
  22
  23static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
  24                       int flags)
  25{
  26        struct sigaction sa;
  27        memset(&sa, 0, sizeof(sa));
  28        sa.sa_sigaction = handler;
  29        sa.sa_flags = SA_SIGINFO | flags;
  30        sigemptyset(&sa.sa_mask);
  31        if (sigaction(sig, &sa, 0))
  32                err(1, "sigaction");
  33}
  34
  35static volatile sig_atomic_t sig_traps;
  36static sigjmp_buf jmpbuf;
  37
  38static volatile sig_atomic_t n_errs;
  39
  40#ifdef __x86_64__
  41#define REG_AX REG_RAX
  42#define REG_IP REG_RIP
  43#else
  44#define REG_AX REG_EAX
  45#define REG_IP REG_EIP
  46#endif
  47
  48static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)
  49{
  50        ucontext_t *ctx = (ucontext_t*)ctx_void;
  51        long ax = (long)ctx->uc_mcontext.gregs[REG_AX];
  52
  53        if (ax != -EFAULT && ax != -ENOSYS) {
  54                printf("[FAIL]\tAX had the wrong value: 0x%lx\n",
  55                       (unsigned long)ax);
  56                n_errs++;
  57        } else {
  58                printf("[OK]\tSeems okay\n");
  59        }
  60
  61        siglongjmp(jmpbuf, 1);
  62}
  63
  64static volatile sig_atomic_t sigtrap_consecutive_syscalls;
  65
  66static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
  67{
  68        /*
  69         * KVM has some bugs that can cause us to stop making progress.
  70         * detect them and complain, but don't infinite loop or fail the
  71         * test.
  72         */
  73
  74        ucontext_t *ctx = (ucontext_t*)ctx_void;
  75        unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
  76
  77        if (*ip == 0x340f || *ip == 0x050f) {
  78                /* The trap was on SYSCALL or SYSENTER */
  79                sigtrap_consecutive_syscalls++;
  80                if (sigtrap_consecutive_syscalls > 3) {
  81                        printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");
  82                        siglongjmp(jmpbuf, 1);
  83                }
  84        } else {
  85                sigtrap_consecutive_syscalls = 0;
  86        }
  87}
  88
  89static void sigill(int sig, siginfo_t *info, void *ctx_void)
  90{
  91        ucontext_t *ctx = (ucontext_t*)ctx_void;
  92        unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
  93
  94        if (*ip == 0x0b0f) {
  95                /* one of the ud2 instructions faulted */
  96                printf("[OK]\tSYSCALL returned normally\n");
  97        } else {
  98                printf("[SKIP]\tIllegal instruction\n");
  99        }
 100        siglongjmp(jmpbuf, 1);
 101}
 102
 103int main()
 104{
 105        stack_t stack = {
 106                .ss_sp = altstack_data,
 107                .ss_size = SIGSTKSZ,
 108        };
 109        if (sigaltstack(&stack, NULL) != 0)
 110                err(1, "sigaltstack");
 111
 112        sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
 113        /*
 114         * The actual exception can vary.  On Atom CPUs, we get #SS
 115         * instead of #PF when the vDSO fails to access the stack when
 116         * ESP is too close to 2^32, and #SS causes SIGBUS.
 117         */
 118        sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);
 119        sethandler(SIGILL, sigill, SA_ONSTACK);
 120
 121        /*
 122         * Exercise another nasty special case.  The 32-bit SYSCALL
 123         * and SYSENTER instructions (even in compat mode) each
 124         * clobber one register.  A Linux system call has a syscall
 125         * number and six arguments, and the user stack pointer
 126         * needs to live in some register on return.  That means
 127         * that we need eight registers, but SYSCALL and SYSENTER
 128         * only preserve seven registers.  As a result, one argument
 129         * ends up on the stack.  The stack is user memory, which
 130         * means that the kernel can fail to read it.
 131         *
 132         * The 32-bit fast system calls don't have a defined ABI:
 133         * we're supposed to invoke them through the vDSO.  So we'll
 134         * fudge it: we set all regs to invalid pointer values and
 135         * invoke the entry instruction.  The return will fail no
 136         * matter what, and we completely lose our program state,
 137         * but we can fix it up with a signal handler.
 138         */
 139
 140        printf("[RUN]\tSYSENTER with invalid state\n");
 141        if (sigsetjmp(jmpbuf, 1) == 0) {
 142                asm volatile (
 143                        "movl $-1, %%eax\n\t"
 144                        "movl $-1, %%ebx\n\t"
 145                        "movl $-1, %%ecx\n\t"
 146                        "movl $-1, %%edx\n\t"
 147                        "movl $-1, %%esi\n\t"
 148                        "movl $-1, %%edi\n\t"
 149                        "movl $-1, %%ebp\n\t"
 150                        "movl $-1, %%esp\n\t"
 151                        "sysenter"
 152                        : : : "memory", "flags");
 153        }
 154
 155        printf("[RUN]\tSYSCALL with invalid state\n");
 156        if (sigsetjmp(jmpbuf, 1) == 0) {
 157                asm volatile (
 158                        "movl $-1, %%eax\n\t"
 159                        "movl $-1, %%ebx\n\t"
 160                        "movl $-1, %%ecx\n\t"
 161                        "movl $-1, %%edx\n\t"
 162                        "movl $-1, %%esi\n\t"
 163                        "movl $-1, %%edi\n\t"
 164                        "movl $-1, %%ebp\n\t"
 165                        "movl $-1, %%esp\n\t"
 166                        "syscall\n\t"
 167                        "ud2"           /* make sure we recover cleanly */
 168                        : : : "memory", "flags");
 169        }
 170
 171        printf("[RUN]\tSYSENTER with TF and invalid state\n");
 172        sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
 173
 174        if (sigsetjmp(jmpbuf, 1) == 0) {
 175                sigtrap_consecutive_syscalls = 0;
 176                set_eflags(get_eflags() | X86_EFLAGS_TF);
 177                asm volatile (
 178                        "movl $-1, %%eax\n\t"
 179                        "movl $-1, %%ebx\n\t"
 180                        "movl $-1, %%ecx\n\t"
 181                        "movl $-1, %%edx\n\t"
 182                        "movl $-1, %%esi\n\t"
 183                        "movl $-1, %%edi\n\t"
 184                        "movl $-1, %%ebp\n\t"
 185                        "movl $-1, %%esp\n\t"
 186                        "sysenter"
 187                        : : : "memory", "flags");
 188        }
 189        set_eflags(get_eflags() & ~X86_EFLAGS_TF);
 190
 191        printf("[RUN]\tSYSCALL with TF and invalid state\n");
 192        if (sigsetjmp(jmpbuf, 1) == 0) {
 193                sigtrap_consecutive_syscalls = 0;
 194                set_eflags(get_eflags() | X86_EFLAGS_TF);
 195                asm volatile (
 196                        "movl $-1, %%eax\n\t"
 197                        "movl $-1, %%ebx\n\t"
 198                        "movl $-1, %%ecx\n\t"
 199                        "movl $-1, %%edx\n\t"
 200                        "movl $-1, %%esi\n\t"
 201                        "movl $-1, %%edi\n\t"
 202                        "movl $-1, %%ebp\n\t"
 203                        "movl $-1, %%esp\n\t"
 204                        "syscall\n\t"
 205                        "ud2"           /* make sure we recover cleanly */
 206                        : : : "memory", "flags");
 207        }
 208        set_eflags(get_eflags() & ~X86_EFLAGS_TF);
 209
 210        return 0;
 211}
 212