Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
0004  * Copyright (c) 2015 Andrew Lutomirski
0005  */
0006 
0007 #define _GNU_SOURCE
0008 
0009 #include <stdlib.h>
0010 #include <stdio.h>
0011 #include <string.h>
0012 #include <sys/signal.h>
0013 #include <sys/ucontext.h>
0014 #include <err.h>
0015 #include <setjmp.h>
0016 #include <errno.h>
0017 
0018 #include "helpers.h"
0019 
0020 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0021                int flags)
0022 {
0023     struct sigaction sa;
0024     memset(&sa, 0, sizeof(sa));
0025     sa.sa_sigaction = handler;
0026     sa.sa_flags = SA_SIGINFO | flags;
0027     sigemptyset(&sa.sa_mask);
0028     if (sigaction(sig, &sa, 0))
0029         err(1, "sigaction");
0030 }
0031 
0032 static volatile sig_atomic_t sig_traps;
0033 static sigjmp_buf jmpbuf;
0034 
0035 static volatile sig_atomic_t n_errs;
0036 
0037 #ifdef __x86_64__
0038 #define REG_AX REG_RAX
0039 #define REG_IP REG_RIP
0040 #else
0041 #define REG_AX REG_EAX
0042 #define REG_IP REG_EIP
0043 #endif
0044 
0045 static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)
0046 {
0047     ucontext_t *ctx = (ucontext_t*)ctx_void;
0048     long ax = (long)ctx->uc_mcontext.gregs[REG_AX];
0049 
0050     if (ax != -EFAULT && ax != -ENOSYS) {
0051         printf("[FAIL]\tAX had the wrong value: 0x%lx\n",
0052                (unsigned long)ax);
0053         printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
0054         n_errs++;
0055     } else {
0056         printf("[OK]\tSeems okay\n");
0057     }
0058 
0059     siglongjmp(jmpbuf, 1);
0060 }
0061 
0062 static volatile sig_atomic_t sigtrap_consecutive_syscalls;
0063 
0064 static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
0065 {
0066     /*
0067      * KVM has some bugs that can cause us to stop making progress.
0068      * detect them and complain, but don't infinite loop or fail the
0069      * test.
0070      */
0071 
0072     ucontext_t *ctx = (ucontext_t*)ctx_void;
0073     unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
0074 
0075     if (*ip == 0x340f || *ip == 0x050f) {
0076         /* The trap was on SYSCALL or SYSENTER */
0077         sigtrap_consecutive_syscalls++;
0078         if (sigtrap_consecutive_syscalls > 3) {
0079             printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");
0080             siglongjmp(jmpbuf, 1);
0081         }
0082     } else {
0083         sigtrap_consecutive_syscalls = 0;
0084     }
0085 }
0086 
0087 static void sigill(int sig, siginfo_t *info, void *ctx_void)
0088 {
0089     ucontext_t *ctx = (ucontext_t*)ctx_void;
0090     unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
0091 
0092     if (*ip == 0x0b0f) {
0093         /* one of the ud2 instructions faulted */
0094         printf("[OK]\tSYSCALL returned normally\n");
0095     } else {
0096         printf("[SKIP]\tIllegal instruction\n");
0097     }
0098     siglongjmp(jmpbuf, 1);
0099 }
0100 
0101 int main()
0102 {
0103     stack_t stack = {
0104         /* Our sigaltstack scratch space. */
0105         .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
0106         .ss_size = SIGSTKSZ,
0107     };
0108     if (sigaltstack(&stack, NULL) != 0)
0109         err(1, "sigaltstack");
0110 
0111     sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
0112     /*
0113      * The actual exception can vary.  On Atom CPUs, we get #SS
0114      * instead of #PF when the vDSO fails to access the stack when
0115      * ESP is too close to 2^32, and #SS causes SIGBUS.
0116      */
0117     sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);
0118     sethandler(SIGILL, sigill, SA_ONSTACK);
0119 
0120     /*
0121      * Exercise another nasty special case.  The 32-bit SYSCALL
0122      * and SYSENTER instructions (even in compat mode) each
0123      * clobber one register.  A Linux system call has a syscall
0124      * number and six arguments, and the user stack pointer
0125      * needs to live in some register on return.  That means
0126      * that we need eight registers, but SYSCALL and SYSENTER
0127      * only preserve seven registers.  As a result, one argument
0128      * ends up on the stack.  The stack is user memory, which
0129      * means that the kernel can fail to read it.
0130      *
0131      * The 32-bit fast system calls don't have a defined ABI:
0132      * we're supposed to invoke them through the vDSO.  So we'll
0133      * fudge it: we set all regs to invalid pointer values and
0134      * invoke the entry instruction.  The return will fail no
0135      * matter what, and we completely lose our program state,
0136      * but we can fix it up with a signal handler.
0137      */
0138 
0139     printf("[RUN]\tSYSENTER with invalid state\n");
0140     if (sigsetjmp(jmpbuf, 1) == 0) {
0141         asm volatile (
0142             "movl $-1, %%eax\n\t"
0143             "movl $-1, %%ebx\n\t"
0144             "movl $-1, %%ecx\n\t"
0145             "movl $-1, %%edx\n\t"
0146             "movl $-1, %%esi\n\t"
0147             "movl $-1, %%edi\n\t"
0148             "movl $-1, %%ebp\n\t"
0149             "movl $-1, %%esp\n\t"
0150             "sysenter"
0151             : : : "memory", "flags");
0152     }
0153 
0154     printf("[RUN]\tSYSCALL with invalid state\n");
0155     if (sigsetjmp(jmpbuf, 1) == 0) {
0156         asm volatile (
0157             "movl $-1, %%eax\n\t"
0158             "movl $-1, %%ebx\n\t"
0159             "movl $-1, %%ecx\n\t"
0160             "movl $-1, %%edx\n\t"
0161             "movl $-1, %%esi\n\t"
0162             "movl $-1, %%edi\n\t"
0163             "movl $-1, %%ebp\n\t"
0164             "movl $-1, %%esp\n\t"
0165             "syscall\n\t"
0166             "ud2"       /* make sure we recover cleanly */
0167             : : : "memory", "flags");
0168     }
0169 
0170     printf("[RUN]\tSYSENTER with TF and invalid state\n");
0171     sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
0172 
0173     if (sigsetjmp(jmpbuf, 1) == 0) {
0174         sigtrap_consecutive_syscalls = 0;
0175         set_eflags(get_eflags() | X86_EFLAGS_TF);
0176         asm volatile (
0177             "movl $-1, %%eax\n\t"
0178             "movl $-1, %%ebx\n\t"
0179             "movl $-1, %%ecx\n\t"
0180             "movl $-1, %%edx\n\t"
0181             "movl $-1, %%esi\n\t"
0182             "movl $-1, %%edi\n\t"
0183             "movl $-1, %%ebp\n\t"
0184             "movl $-1, %%esp\n\t"
0185             "sysenter"
0186             : : : "memory", "flags");
0187     }
0188     set_eflags(get_eflags() & ~X86_EFLAGS_TF);
0189 
0190     printf("[RUN]\tSYSCALL with TF and invalid state\n");
0191     if (sigsetjmp(jmpbuf, 1) == 0) {
0192         sigtrap_consecutive_syscalls = 0;
0193         set_eflags(get_eflags() | X86_EFLAGS_TF);
0194         asm volatile (
0195             "movl $-1, %%eax\n\t"
0196             "movl $-1, %%ebx\n\t"
0197             "movl $-1, %%ecx\n\t"
0198             "movl $-1, %%edx\n\t"
0199             "movl $-1, %%esi\n\t"
0200             "movl $-1, %%edi\n\t"
0201             "movl $-1, %%ebp\n\t"
0202             "movl $-1, %%esp\n\t"
0203             "syscall\n\t"
0204             "ud2"       /* make sure we recover cleanly */
0205             : : : "memory", "flags");
0206     }
0207     set_eflags(get_eflags() & ~X86_EFLAGS_TF);
0208 
0209 #ifdef __x86_64__
0210     printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n");
0211 
0212     if (sigsetjmp(jmpbuf, 1) == 0) {
0213         sigtrap_consecutive_syscalls = 0;
0214 
0215         asm volatile ("wrgsbase %%rax\n\t"
0216                   :: "a" (0xffffffffffff0000UL));
0217 
0218         set_eflags(get_eflags() | X86_EFLAGS_TF);
0219         asm volatile (
0220             "movl $-1, %%eax\n\t"
0221             "movl $-1, %%ebx\n\t"
0222             "movl $-1, %%ecx\n\t"
0223             "movl $-1, %%edx\n\t"
0224             "movl $-1, %%esi\n\t"
0225             "movl $-1, %%edi\n\t"
0226             "movl $-1, %%ebp\n\t"
0227             "movl $-1, %%esp\n\t"
0228             "sysenter"
0229             : : : "memory", "flags");
0230     }
0231     set_eflags(get_eflags() & ~X86_EFLAGS_TF);
0232 #endif
0233 
0234     free(stack.ss_sp);
0235     return 0;
0236 }