~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/x86/syscall_arg_fault.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php