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