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

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

Version: ~ [ linux-6.1-rc7 ] ~ [ linux-6.0.10 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.80 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.156 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.225 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.267 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.300 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.334 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ 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 /* Our sigaltstack scratch space. */
 21 static unsigned char altstack_data[SIGSTKSZ];
 22 
 23 static 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 
 35 static volatile sig_atomic_t sig_traps;
 36 static sigjmp_buf jmpbuf;
 37 
 38 static 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 
 48 static 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 
 64 static volatile sig_atomic_t sigtrap_consecutive_syscalls;
 65 
 66 static 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 
 89 static 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 
103 int 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 

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | 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.

osdn.jp