aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/x86
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-03 21:59:10 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-03 21:59:10 -0500
commita75a3f6fc92888e4119744d8594ffdf748c3d444 (patch)
tree06c344beb369b1067a5621a175fae04508ba8d0d /tools/testing/selftests/x86
parentd2bea739f8b41d620c235d81e00289d01169dc3c (diff)
parent3bd29515d1cad26fa85a1a9b442de8816c1f5c54 (diff)
Merge branch 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 asm changes from Ingo Molnar: "The main change in this cycle is another step in the big x86 system call interface rework by Andy Lutomirski, which moves most of the low level x86 entry code from assembly to C, for all syscall entries except native 64-bit system calls: arch/x86/entry/entry_32.S | 182 ++++------ arch/x86/entry/entry_64_compat.S | 547 ++++++++----------------------- 194 insertions(+), 535 deletions(-) ... our hope is that the final remaining step (converting native 64-bit system calls) will be less painful as all the previous steps, given that most of the legacies and quirks are concentrated around native 32-bit and compat environments" * 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (47 commits) x86/entry/32: Fix FS and GS restore in opportunistic SYSEXIT x86/entry/32: Fix entry_INT80_32() to expect interrupts to be on um/x86: Fix build after x86 syscall changes x86/asm: Remove the xyz_cfi macros from dwarf2.h selftests/x86: Style fixes for the 'unwind_vdso' test x86/entry/64/compat: Document sysenter_fix_flags's reason for existence x86/entry: Split and inline syscall_return_slowpath() x86/entry: Split and inline prepare_exit_to_usermode() x86/entry: Use pt_regs_to_thread_info() in syscall entry tracing x86/entry: Hide two syscall entry assertions behind CONFIG_DEBUG_ENTRY x86/entry: Micro-optimize compat fast syscall arg fetch x86/entry: Force inlining of 32-bit syscall code x86/entry: Make irqs_disabled checks in exit code depend on lockdep x86/entry: Remove unnecessary IRQ twiddling in fast 32-bit syscalls x86/asm: Remove thread_info.sysenter_return x86/entry/32: Re-implement SYSENTER using the new C path x86/entry/32: Switch INT80 to the new C syscall path x86/entry/32: Open-code return tracking from fork and kthreads x86/entry/compat: Implement opportunistic SYSRETL for compat syscalls x86/vdso/compat: Wire up SYSENTER and SYSCSALL for compat userspace ...
Diffstat (limited to 'tools/testing/selftests/x86')
-rw-r--r--tools/testing/selftests/x86/Makefile6
-rw-r--r--tools/testing/selftests/x86/ptrace_syscall.c294
-rw-r--r--tools/testing/selftests/x86/raw_syscall_helper_32.S46
-rw-r--r--tools/testing/selftests/x86/test_syscall_vdso.c401
-rw-r--r--tools/testing/selftests/x86/thunks_32.S55
-rw-r--r--tools/testing/selftests/x86/unwind_vdso.c211
6 files changed, 1011 insertions, 2 deletions
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 29089b24d18b..389701f59940 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -4,8 +4,8 @@ include ../lib.mk
4 4
5.PHONY: all all_32 all_64 warn_32bit_failure clean 5.PHONY: all all_32 all_64 warn_32bit_failure clean
6 6
7TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt 7TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt ptrace_syscall
8TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn 8TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso
9 9
10TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) 10TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
11BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) 11BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
@@ -60,3 +60,5 @@ endif
60 60
61# Some tests have additional dependencies. 61# Some tests have additional dependencies.
62sysret_ss_attrs_64: thunks.S 62sysret_ss_attrs_64: thunks.S
63ptrace_syscall_32: raw_syscall_helper_32.S
64test_syscall_vdso_32: thunks_32.S
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c
new file mode 100644
index 000000000000..5105b49cd8aa
--- /dev/null
+++ b/tools/testing/selftests/x86/ptrace_syscall.c
@@ -0,0 +1,294 @@
1#define _GNU_SOURCE
2
3#include <sys/ptrace.h>
4#include <sys/types.h>
5#include <sys/wait.h>
6#include <sys/syscall.h>
7#include <sys/user.h>
8#include <unistd.h>
9#include <errno.h>
10#include <stddef.h>
11#include <stdio.h>
12#include <err.h>
13#include <string.h>
14#include <asm/ptrace-abi.h>
15#include <sys/auxv.h>
16
17/* Bitness-agnostic defines for user_regs_struct fields. */
18#ifdef __x86_64__
19# define user_syscall_nr orig_rax
20# define user_arg0 rdi
21# define user_arg1 rsi
22# define user_arg2 rdx
23# define user_arg3 r10
24# define user_arg4 r8
25# define user_arg5 r9
26# define user_ip rip
27# define user_ax rax
28#else
29# define user_syscall_nr orig_eax
30# define user_arg0 ebx
31# define user_arg1 ecx
32# define user_arg2 edx
33# define user_arg3 esi
34# define user_arg4 edi
35# define user_arg5 ebp
36# define user_ip eip
37# define user_ax eax
38#endif
39
40static int nerrs = 0;
41
42struct syscall_args32 {
43 uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5;
44};
45
46#ifdef __i386__
47extern void sys32_helper(struct syscall_args32 *, void *);
48extern void int80_and_ret(void);
49#endif
50
51/*
52 * Helper to invoke int80 with controlled regs and capture the final regs.
53 */
54static void do_full_int80(struct syscall_args32 *args)
55{
56#ifdef __x86_64__
57 register unsigned long bp asm("bp") = args->arg5;
58 asm volatile ("int $0x80"
59 : "+a" (args->nr),
60 "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2),
61 "+S" (args->arg3), "+D" (args->arg4), "+r" (bp));
62 args->arg5 = bp;
63#else
64 sys32_helper(args, int80_and_ret);
65#endif
66}
67
68#ifdef __i386__
69static void (*vsyscall32)(void);
70
71/*
72 * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with
73 * controlled regs and capture the final regs. This is so nasty that it
74 * crashes my copy of gdb :)
75 */
76static void do_full_vsyscall32(struct syscall_args32 *args)
77{
78 sys32_helper(args, vsyscall32);
79}
80#endif
81
82static siginfo_t wait_trap(pid_t chld)
83{
84 siginfo_t si;
85 if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
86 err(1, "waitid");
87 if (si.si_pid != chld)
88 errx(1, "got unexpected pid in event\n");
89 if (si.si_code != CLD_TRAPPED)
90 errx(1, "got unexpected event type %d\n", si.si_code);
91 return si;
92}
93
94static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
95 int flags)
96{
97 struct sigaction sa;
98 memset(&sa, 0, sizeof(sa));
99 sa.sa_sigaction = handler;
100 sa.sa_flags = SA_SIGINFO | flags;
101 sigemptyset(&sa.sa_mask);
102 if (sigaction(sig, &sa, 0))
103 err(1, "sigaction");
104}
105
106static void clearhandler(int sig)
107{
108 struct sigaction sa;
109 memset(&sa, 0, sizeof(sa));
110 sa.sa_handler = SIG_DFL;
111 sigemptyset(&sa.sa_mask);
112 if (sigaction(sig, &sa, 0))
113 err(1, "sigaction");
114}
115
116#ifdef __x86_64__
117# define REG_BP REG_RBP
118#else
119# define REG_BP REG_EBP
120#endif
121
122static void empty_handler(int sig, siginfo_t *si, void *ctx_void)
123{
124}
125
126static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *))
127{
128 struct syscall_args32 args = {
129 .nr = 224, /* gettid */
130 .arg0 = 10, .arg1 = 11, .arg2 = 12,
131 .arg3 = 13, .arg4 = 14, .arg5 = 15,
132 };
133
134 do_syscall(&args);
135
136 if (args.nr != getpid() ||
137 args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 ||
138 args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
139 printf("[FAIL]\tgetpid() failed to preseve regs\n");
140 nerrs++;
141 } else {
142 printf("[OK]\tgetpid() preserves regs\n");
143 }
144
145 sethandler(SIGUSR1, empty_handler, 0);
146
147 args.nr = 37; /* kill */
148 args.arg0 = getpid();
149 args.arg1 = SIGUSR1;
150 do_syscall(&args);
151 if (args.nr != 0 ||
152 args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 ||
153 args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
154 printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preseve regs\n");
155 nerrs++;
156 } else {
157 printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n");
158 }
159 clearhandler(SIGUSR1);
160}
161
162static void test_ptrace_syscall_restart(void)
163{
164 printf("[RUN]\tptrace-induced syscall restart\n");
165 pid_t chld = fork();
166 if (chld < 0)
167 err(1, "fork");
168
169 if (chld == 0) {
170 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
171 err(1, "PTRACE_TRACEME");
172
173 printf("\tChild will make one syscall\n");
174 raise(SIGSTOP);
175
176 syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
177 _exit(0);
178 }
179
180 int status;
181
182 /* Wait for SIGSTOP. */
183 if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
184 err(1, "waitpid");
185
186 struct user_regs_struct regs;
187
188 printf("[RUN]\tSYSEMU\n");
189 if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
190 err(1, "PTRACE_SYSCALL");
191 wait_trap(chld);
192
193 if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
194 err(1, "PTRACE_GETREGS");
195
196 if (regs.user_syscall_nr != SYS_gettid ||
197 regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
198 regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
199 regs.user_arg4 != 14 || regs.user_arg5 != 15) {
200 printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
201 nerrs++;
202 } else {
203 printf("[OK]\tInitial nr and args are correct\n");
204 }
205
206 printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
207 (unsigned long)regs.user_ip);
208
209 /*
210 * This does exactly what it appears to do if syscall is int80 or
211 * SYSCALL64. For SYSCALL32 or SYSENTER, though, this is highly
212 * magical. It needs to work so that ptrace and syscall restart
213 * work as expected.
214 */
215 regs.user_ax = regs.user_syscall_nr;
216 regs.user_ip -= 2;
217 if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
218 err(1, "PTRACE_SETREGS");
219
220 if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
221 err(1, "PTRACE_SYSCALL");
222 wait_trap(chld);
223
224 if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
225 err(1, "PTRACE_GETREGS");
226
227 if (regs.user_syscall_nr != SYS_gettid ||
228 regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
229 regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
230 regs.user_arg4 != 14 || regs.user_arg5 != 15) {
231 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
232 nerrs++;
233 } else {
234 printf("[OK]\tRestarted nr and args are correct\n");
235 }
236
237 printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
238 (unsigned long)regs.user_ip);
239
240 regs.user_ax = SYS_getpid;
241 regs.user_arg0 = 20;
242 regs.user_arg1 = 21;
243 regs.user_arg2 = 22;
244 regs.user_arg3 = 23;
245 regs.user_arg4 = 24;
246 regs.user_arg5 = 25;
247 regs.user_ip -= 2;
248
249 if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
250 err(1, "PTRACE_SETREGS");
251
252 if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
253 err(1, "PTRACE_SYSCALL");
254 wait_trap(chld);
255
256 if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
257 err(1, "PTRACE_GETREGS");
258
259 if (regs.user_syscall_nr != SYS_getpid ||
260 regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 ||
261 regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
262 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
263 nerrs++;
264 } else {
265 printf("[OK]\tReplacement nr and args are correct\n");
266 }
267
268 if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
269 err(1, "PTRACE_CONT");
270 if (waitpid(chld, &status, 0) != chld)
271 err(1, "waitpid");
272 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
273 printf("[FAIL]\tChild failed\n");
274 nerrs++;
275 } else {
276 printf("[OK]\tChild exited cleanly\n");
277 }
278}
279
280int main()
281{
282 printf("[RUN]\tCheck int80 return regs\n");
283 test_sys32_regs(do_full_int80);
284
285#if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16)
286 vsyscall32 = (void *)getauxval(AT_SYSINFO);
287 printf("[RUN]\tCheck AT_SYSINFO return regs\n");
288 test_sys32_regs(do_full_vsyscall32);
289#endif
290
291 test_ptrace_syscall_restart();
292
293 return 0;
294}
diff --git a/tools/testing/selftests/x86/raw_syscall_helper_32.S b/tools/testing/selftests/x86/raw_syscall_helper_32.S
new file mode 100644
index 000000000000..534e71e35c6a
--- /dev/null
+++ b/tools/testing/selftests/x86/raw_syscall_helper_32.S
@@ -0,0 +1,46 @@
1.global sys32_helper
2sys32_helper:
3 /* Args: syscall_args_32*, function pointer */
4 pushl %ebp
5 pushl %ebx
6 pushl %esi
7 pushl %edi
8 movl 5*4(%esp), %eax /* pointer to args struct */
9
10 movl 1*4(%eax), %ebx
11 movl 2*4(%eax), %ecx
12 movl 3*4(%eax), %edx
13 movl 4*4(%eax), %esi
14 movl 5*4(%eax), %edi
15 movl 6*4(%eax), %ebp
16 movl 0*4(%eax), %eax
17
18 call *(6*4)(%esp) /* Do the syscall */
19
20 /* Now we need to recover without losing any reg values */
21 pushl %eax
22 movl 6*4(%esp), %eax
23 popl 0*4(%eax)
24 movl %ebx, 1*4(%eax)
25 movl %ecx, 2*4(%eax)
26 movl %edx, 3*4(%eax)
27 movl %esi, 4*4(%eax)
28 movl %edi, 5*4(%eax)
29 movl %ebp, 6*4(%eax)
30
31 popl %edi
32 popl %esi
33 popl %ebx
34 popl %ebp
35 ret
36
37 .type sys32_helper, @function
38 .size sys32_helper, .-sys32_helper
39
40.global int80_and_ret
41int80_and_ret:
42 int $0x80
43 ret
44
45 .type int80_and_ret, @function
46 .size int80_and_ret, .-int80_and_ret
diff --git a/tools/testing/selftests/x86/test_syscall_vdso.c b/tools/testing/selftests/x86/test_syscall_vdso.c
new file mode 100644
index 000000000000..40370354d4c1
--- /dev/null
+++ b/tools/testing/selftests/x86/test_syscall_vdso.c
@@ -0,0 +1,401 @@
1/*
2 * 32-bit syscall ABI conformance test.
3 *
4 * Copyright (c) 2015 Denys Vlasenko
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 */
15/*
16 * Can be built statically:
17 * gcc -Os -Wall -static -m32 test_syscall_vdso.c thunks_32.S
18 */
19#undef _GNU_SOURCE
20#define _GNU_SOURCE 1
21#undef __USE_GNU
22#define __USE_GNU 1
23#include <unistd.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <signal.h>
28#include <sys/types.h>
29#include <sys/select.h>
30#include <sys/time.h>
31#include <elf.h>
32#include <sys/ptrace.h>
33#include <sys/wait.h>
34
35#if !defined(__i386__)
36int main(int argc, char **argv, char **envp)
37{
38 printf("[SKIP]\tNot a 32-bit x86 userspace\n");
39 return 0;
40}
41#else
42
43long syscall_addr;
44long get_syscall(char **envp)
45{
46 Elf32_auxv_t *auxv;
47 while (*envp++ != NULL)
48 continue;
49 for (auxv = (void *)envp; auxv->a_type != AT_NULL; auxv++)
50 if (auxv->a_type == AT_SYSINFO)
51 return auxv->a_un.a_val;
52 printf("[WARN]\tAT_SYSINFO not supplied\n");
53 return 0;
54}
55
56asm (
57 " .pushsection .text\n"
58 " .global int80\n"
59 "int80:\n"
60 " int $0x80\n"
61 " ret\n"
62 " .popsection\n"
63);
64extern char int80;
65
66struct regs64 {
67 uint64_t rax, rbx, rcx, rdx;
68 uint64_t rsi, rdi, rbp, rsp;
69 uint64_t r8, r9, r10, r11;
70 uint64_t r12, r13, r14, r15;
71};
72struct regs64 regs64;
73int kernel_is_64bit;
74
75asm (
76 " .pushsection .text\n"
77 " .code64\n"
78 "get_regs64:\n"
79 " push %rax\n"
80 " mov $regs64, %eax\n"
81 " pop 0*8(%rax)\n"
82 " movq %rbx, 1*8(%rax)\n"
83 " movq %rcx, 2*8(%rax)\n"
84 " movq %rdx, 3*8(%rax)\n"
85 " movq %rsi, 4*8(%rax)\n"
86 " movq %rdi, 5*8(%rax)\n"
87 " movq %rbp, 6*8(%rax)\n"
88 " movq %rsp, 7*8(%rax)\n"
89 " movq %r8, 8*8(%rax)\n"
90 " movq %r9, 9*8(%rax)\n"
91 " movq %r10, 10*8(%rax)\n"
92 " movq %r11, 11*8(%rax)\n"
93 " movq %r12, 12*8(%rax)\n"
94 " movq %r13, 13*8(%rax)\n"
95 " movq %r14, 14*8(%rax)\n"
96 " movq %r15, 15*8(%rax)\n"
97 " ret\n"
98 "poison_regs64:\n"
99 " movq $0x7f7f7f7f, %r8\n"
100 " shl $32, %r8\n"
101 " orq $0x7f7f7f7f, %r8\n"
102 " movq %r8, %r9\n"
103 " movq %r8, %r10\n"
104 " movq %r8, %r11\n"
105 " movq %r8, %r12\n"
106 " movq %r8, %r13\n"
107 " movq %r8, %r14\n"
108 " movq %r8, %r15\n"
109 " ret\n"
110 " .code32\n"
111 " .popsection\n"
112);
113extern void get_regs64(void);
114extern void poison_regs64(void);
115extern unsigned long call64_from_32(void (*function)(void));
116void print_regs64(void)
117{
118 if (!kernel_is_64bit)
119 return;
120 printf("ax:%016llx bx:%016llx cx:%016llx dx:%016llx\n", regs64.rax, regs64.rbx, regs64.rcx, regs64.rdx);
121 printf("si:%016llx di:%016llx bp:%016llx sp:%016llx\n", regs64.rsi, regs64.rdi, regs64.rbp, regs64.rsp);
122 printf(" 8:%016llx 9:%016llx 10:%016llx 11:%016llx\n", regs64.r8 , regs64.r9 , regs64.r10, regs64.r11);
123 printf("12:%016llx 13:%016llx 14:%016llx 15:%016llx\n", regs64.r12, regs64.r13, regs64.r14, regs64.r15);
124}
125
126int check_regs64(void)
127{
128 int err = 0;
129 int num = 8;
130 uint64_t *r64 = &regs64.r8;
131
132 if (!kernel_is_64bit)
133 return 0;
134
135 do {
136 if (*r64 == 0x7f7f7f7f7f7f7f7fULL)
137 continue; /* register did not change */
138 if (syscall_addr != (long)&int80) {
139 /*
140 * Non-INT80 syscall entrypoints are allowed to clobber R8+ regs:
141 * either clear them to 0, or for R11, load EFLAGS.
142 */
143 if (*r64 == 0)
144 continue;
145 if (num == 11) {
146 printf("[NOTE]\tR11 has changed:%016llx - assuming clobbered by SYSRET insn\n", *r64);
147 continue;
148 }
149 } else {
150 /* INT80 syscall entrypoint can be used by
151 * 64-bit programs too, unlike SYSCALL/SYSENTER.
152 * Therefore it must preserve R12+
153 * (they are callee-saved registers in 64-bit C ABI).
154 *
155 * This was probably historically not intended,
156 * but R8..11 are clobbered (cleared to 0).
157 * IOW: they are the only registers which aren't
158 * preserved across INT80 syscall.
159 */
160 if (*r64 == 0 && num <= 11)
161 continue;
162 }
163 printf("[FAIL]\tR%d has changed:%016llx\n", num, *r64);
164 err++;
165 } while (r64++, ++num < 16);
166
167 if (!err)
168 printf("[OK]\tR8..R15 did not leak kernel data\n");
169 return err;
170}
171
172int nfds;
173fd_set rfds;
174fd_set wfds;
175fd_set efds;
176struct timespec timeout;
177sigset_t sigmask;
178struct {
179 sigset_t *sp;
180 int sz;
181} sigmask_desc;
182
183void prep_args()
184{
185 nfds = 42;
186 FD_ZERO(&rfds);
187 FD_ZERO(&wfds);
188 FD_ZERO(&efds);
189 FD_SET(0, &rfds);
190 FD_SET(1, &wfds);
191 FD_SET(2, &efds);
192 timeout.tv_sec = 0;
193 timeout.tv_nsec = 123;
194 sigemptyset(&sigmask);
195 sigaddset(&sigmask, SIGINT);
196 sigaddset(&sigmask, SIGUSR2);
197 sigaddset(&sigmask, SIGRTMAX);
198 sigmask_desc.sp = &sigmask;
199 sigmask_desc.sz = 8; /* bytes */
200}
201
202static void print_flags(const char *name, unsigned long r)
203{
204 static const char *bitarray[] = {
205 "\n" ,"c\n" ,/* Carry Flag */
206 "0 " ,"1 " ,/* Bit 1 - always on */
207 "" ,"p " ,/* Parity Flag */
208 "0 " ,"3? " ,
209 "" ,"a " ,/* Auxiliary carry Flag */
210 "0 " ,"5? " ,
211 "" ,"z " ,/* Zero Flag */
212 "" ,"s " ,/* Sign Flag */
213 "" ,"t " ,/* Trap Flag */
214 "" ,"i " ,/* Interrupt Flag */
215 "" ,"d " ,/* Direction Flag */
216 "" ,"o " ,/* Overflow Flag */
217 "0 " ,"1 " ,/* I/O Privilege Level (2 bits) */
218 "0" ,"1" ,/* I/O Privilege Level (2 bits) */
219 "" ,"n " ,/* Nested Task */
220 "0 " ,"15? ",
221 "" ,"r " ,/* Resume Flag */
222 "" ,"v " ,/* Virtual Mode */
223 "" ,"ac " ,/* Alignment Check/Access Control */
224 "" ,"vif ",/* Virtual Interrupt Flag */
225 "" ,"vip ",/* Virtual Interrupt Pending */
226 "" ,"id " ,/* CPUID detection */
227 NULL
228 };
229 const char **bitstr;
230 int bit;
231
232 printf("%s=%016lx ", name, r);
233 bitstr = bitarray + 42;
234 bit = 21;
235 if ((r >> 22) != 0)
236 printf("(extra bits are set) ");
237 do {
238 if (bitstr[(r >> bit) & 1][0])
239 fputs(bitstr[(r >> bit) & 1], stdout);
240 bitstr -= 2;
241 bit--;
242 } while (bit >= 0);
243}
244
245int run_syscall(void)
246{
247 long flags, bad_arg;
248
249 prep_args();
250
251 if (kernel_is_64bit)
252 call64_from_32(poison_regs64);
253 /*print_regs64();*/
254
255 asm("\n"
256 /* Try 6-arg syscall: pselect. It should return quickly */
257 " push %%ebp\n"
258 " mov $308, %%eax\n" /* PSELECT */
259 " mov nfds, %%ebx\n" /* ebx arg1 */
260 " mov $rfds, %%ecx\n" /* ecx arg2 */
261 " mov $wfds, %%edx\n" /* edx arg3 */
262 " mov $efds, %%esi\n" /* esi arg4 */
263 " mov $timeout, %%edi\n" /* edi arg5 */
264 " mov $sigmask_desc, %%ebp\n" /* %ebp arg6 */
265 " push $0x200ed7\n" /* set almost all flags */
266 " popf\n" /* except TF, IOPL, NT, RF, VM, AC, VIF, VIP */
267 " call *syscall_addr\n"
268 /* Check that registers are not clobbered */
269 " pushf\n"
270 " pop %%eax\n"
271 " cld\n"
272 " cmp nfds, %%ebx\n" /* ebx arg1 */
273 " mov $1, %%ebx\n"
274 " jne 1f\n"
275 " cmp $rfds, %%ecx\n" /* ecx arg2 */
276 " mov $2, %%ebx\n"
277 " jne 1f\n"
278 " cmp $wfds, %%edx\n" /* edx arg3 */
279 " mov $3, %%ebx\n"
280 " jne 1f\n"
281 " cmp $efds, %%esi\n" /* esi arg4 */
282 " mov $4, %%ebx\n"
283 " jne 1f\n"
284 " cmp $timeout, %%edi\n" /* edi arg5 */
285 " mov $5, %%ebx\n"
286 " jne 1f\n"
287 " cmpl $sigmask_desc, %%ebp\n" /* %ebp arg6 */
288 " mov $6, %%ebx\n"
289 " jne 1f\n"
290 " mov $0, %%ebx\n"
291 "1:\n"
292 " pop %%ebp\n"
293 : "=a" (flags), "=b" (bad_arg)
294 :
295 : "cx", "dx", "si", "di"
296 );
297
298 if (kernel_is_64bit) {
299 memset(&regs64, 0x77, sizeof(regs64));
300 call64_from_32(get_regs64);
301 /*print_regs64();*/
302 }
303
304 /*
305 * On paravirt kernels, flags are not preserved across syscalls.
306 * Thus, we do not consider it a bug if some are changed.
307 * We just show ones which do.
308 */
309 if ((0x200ed7 ^ flags) != 0) {
310 print_flags("[WARN]\tFlags before", 0x200ed7);
311 print_flags("[WARN]\tFlags after", flags);
312 print_flags("[WARN]\tFlags change", (0x200ed7 ^ flags));
313 }
314
315 if (bad_arg) {
316 printf("[FAIL]\targ#%ld clobbered\n", bad_arg);
317 return 1;
318 }
319 printf("[OK]\tArguments are preserved across syscall\n");
320
321 return check_regs64();
322}
323
324int run_syscall_twice()
325{
326 int exitcode = 0;
327 long sv;
328
329 if (syscall_addr) {
330 printf("[RUN]\tExecuting 6-argument 32-bit syscall via VDSO\n");
331 exitcode = run_syscall();
332 }
333 sv = syscall_addr;
334 syscall_addr = (long)&int80;
335 printf("[RUN]\tExecuting 6-argument 32-bit syscall via INT 80\n");
336 exitcode += run_syscall();
337 syscall_addr = sv;
338 return exitcode;
339}
340
341void ptrace_me()
342{
343 pid_t pid;
344
345 fflush(NULL);
346 pid = fork();
347 if (pid < 0)
348 exit(1);
349 if (pid == 0) {
350 /* child */
351 if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) != 0)
352 exit(0);
353 raise(SIGSTOP);
354 return;
355 }
356 /* parent */
357 printf("[RUN]\tRunning tests under ptrace\n");
358 while (1) {
359 int status;
360 pid = waitpid(-1, &status, __WALL);
361 if (WIFEXITED(status))
362 exit(WEXITSTATUS(status));
363 if (WIFSIGNALED(status))
364 exit(WTERMSIG(status));
365 if (pid <= 0 || !WIFSTOPPED(status)) /* paranoia */
366 exit(255);
367 /*
368 * Note: we do not inject sig = WSTOPSIG(status).
369 * We probably should, but careful: do not inject SIGTRAP
370 * generated by syscall entry/exit stops.
371 * That kills the child.
372 */
373 ptrace(PTRACE_SYSCALL, pid, 0L, 0L /*sig*/);
374 }
375}
376
377int main(int argc, char **argv, char **envp)
378{
379 int exitcode = 0;
380 int cs;
381
382 asm("\n"
383 " movl %%cs, %%eax\n"
384 : "=a" (cs)
385 );
386 kernel_is_64bit = (cs == 0x23);
387 if (!kernel_is_64bit)
388 printf("[NOTE]\tNot a 64-bit kernel, won't test R8..R15 leaks\n");
389
390 /* This only works for non-static builds:
391 * syscall_addr = dlsym(dlopen("linux-gate.so.1", RTLD_NOW), "__kernel_vsyscall");
392 */
393 syscall_addr = get_syscall(envp);
394
395 exitcode += run_syscall_twice();
396 ptrace_me();
397 exitcode += run_syscall_twice();
398
399 return exitcode;
400}
401#endif
diff --git a/tools/testing/selftests/x86/thunks_32.S b/tools/testing/selftests/x86/thunks_32.S
new file mode 100644
index 000000000000..29b644bb9f2f
--- /dev/null
+++ b/tools/testing/selftests/x86/thunks_32.S
@@ -0,0 +1,55 @@
1/*
2 * thunks_32.S - assembly helpers for mixed-bitness code
3 * Copyright (c) 2015 Denys Vlasenko
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * These are little helpers that make it easier to switch bitness on
15 * the fly.
16 */
17
18 .text
19 .code32
20
21 .global call64_from_32
22 .type call32_from_64, @function
23
24 // 4(%esp): function to call
25call64_from_32:
26 // Fetch function address
27 mov 4(%esp), %eax
28
29 // Save registers which are callee-clobbered by 64-bit ABI
30 push %ecx
31 push %edx
32 push %esi
33 push %edi
34
35 // Switch to long mode
36 jmp $0x33,$1f
371: .code64
38
39 // Call the function
40 call *%rax
41
42 // Switch to compatibility mode
43 push $0x23 /* USER32_CS */
44 .code32; push $1f; .code64 /* hack: can't have X86_64_32S relocation in 32-bit ELF */
45 lretq
461: .code32
47
48 pop %edi
49 pop %esi
50 pop %edx
51 pop %ecx
52
53 ret
54
55.size call64_from_32, .-call64_from_32
diff --git a/tools/testing/selftests/x86/unwind_vdso.c b/tools/testing/selftests/x86/unwind_vdso.c
new file mode 100644
index 000000000000..00a26a82fa98
--- /dev/null
+++ b/tools/testing/selftests/x86/unwind_vdso.c
@@ -0,0 +1,211 @@
1/*
2 * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
3 * Copyright (c) 2014-2015 Andrew Lutomirski
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * This tests __kernel_vsyscall's unwind info.
15 */
16
17#define _GNU_SOURCE
18
19#include <features.h>
20#include <stdio.h>
21
22#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16
23
24int main()
25{
26 /* We need getauxval(). */
27 printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");
28 return 0;
29}
30
31#else
32
33#include <sys/time.h>
34#include <stdlib.h>
35#include <syscall.h>
36#include <unistd.h>
37#include <string.h>
38#include <inttypes.h>
39#include <sys/mman.h>
40#include <signal.h>
41#include <sys/ucontext.h>
42#include <err.h>
43#include <stddef.h>
44#include <stdbool.h>
45#include <sys/ptrace.h>
46#include <sys/user.h>
47#include <sys/ucontext.h>
48#include <link.h>
49#include <sys/auxv.h>
50#include <dlfcn.h>
51#include <unwind.h>
52
53static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
54 int flags)
55{
56 struct sigaction sa;
57 memset(&sa, 0, sizeof(sa));
58 sa.sa_sigaction = handler;
59 sa.sa_flags = SA_SIGINFO | flags;
60 sigemptyset(&sa.sa_mask);
61 if (sigaction(sig, &sa, 0))
62 err(1, "sigaction");
63}
64
65#ifdef __x86_64__
66# define WIDTH "q"
67#else
68# define WIDTH "l"
69#endif
70
71static unsigned long get_eflags(void)
72{
73 unsigned long eflags;
74 asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags));
75 return eflags;
76}
77
78static void set_eflags(unsigned long eflags)
79{
80 asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH
81 : : "rm" (eflags) : "flags");
82}
83
84#define X86_EFLAGS_TF (1UL << 8)
85
86static volatile sig_atomic_t nerrs;
87static unsigned long sysinfo;
88static bool got_sysinfo = false;
89static unsigned long return_address;
90
91struct unwind_state {
92 unsigned long ip; /* trap source */
93 int depth; /* -1 until we hit the trap source */
94};
95
96_Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)
97{
98 struct unwind_state *state = opaque;
99 unsigned long ip = _Unwind_GetIP(ctx);
100
101 if (state->depth == -1) {
102 if (ip == state->ip)
103 state->depth = 0;
104 else
105 return _URC_NO_REASON; /* Not there yet */
106 }
107 printf("\t 0x%lx\n", ip);
108
109 if (ip == return_address) {
110 /* Here we are. */
111 unsigned long eax = _Unwind_GetGR(ctx, 0);
112 unsigned long ecx = _Unwind_GetGR(ctx, 1);
113 unsigned long edx = _Unwind_GetGR(ctx, 2);
114 unsigned long ebx = _Unwind_GetGR(ctx, 3);
115 unsigned long ebp = _Unwind_GetGR(ctx, 5);
116 unsigned long esi = _Unwind_GetGR(ctx, 6);
117 unsigned long edi = _Unwind_GetGR(ctx, 7);
118 bool ok = (eax == SYS_getpid || eax == getpid()) &&
119 ebx == 1 && ecx == 2 && edx == 3 &&
120 esi == 4 && edi == 5 && ebp == 6;
121
122 if (!ok)
123 nerrs++;
124 printf("[%s]\t NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",
125 (ok ? "OK" : "FAIL"),
126 eax, ebx, ecx, edx, esi, edi, ebp);
127
128 return _URC_NORMAL_STOP;
129 } else {
130 state->depth++;
131 return _URC_NO_REASON;
132 }
133}
134
135static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
136{
137 ucontext_t *ctx = (ucontext_t *)ctx_void;
138 struct unwind_state state;
139 unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];
140
141 if (!got_sysinfo && ip == sysinfo) {
142 got_sysinfo = true;
143
144 /* Find the return address. */
145 return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];
146
147 printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",
148 ip, return_address);
149 }
150
151 if (!got_sysinfo)
152 return; /* Not there yet */
153
154 if (ip == return_address) {
155 ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
156 printf("\tVsyscall is done\n");
157 return;
158 }
159
160 printf("\tSIGTRAP at 0x%lx\n", ip);
161
162 state.ip = ip;
163 state.depth = -1;
164 _Unwind_Backtrace(trace_fn, &state);
165}
166
167int main()
168{
169 sysinfo = getauxval(AT_SYSINFO);
170 printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);
171
172 Dl_info info;
173 if (!dladdr((void *)sysinfo, &info)) {
174 printf("[WARN]\tdladdr failed on AT_SYSINFO\n");
175 } else {
176 printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",
177 info.dli_fname, info.dli_fbase);
178 }
179
180 sethandler(SIGTRAP, sigtrap, 0);
181
182 syscall(SYS_getpid); /* Force symbol binding without TF set. */
183 printf("[RUN]\tSet TF and check a fast syscall\n");
184 set_eflags(get_eflags() | X86_EFLAGS_TF);
185 syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);
186 if (!got_sysinfo) {
187 set_eflags(get_eflags() & ~X86_EFLAGS_TF);
188
189 /*
190 * The most likely cause of this is that you're on Debian or
191 * a Debian-based distro, you're missing libc6-i686, and you're
192 * affected by libc/19006 (https://sourceware.org/PR19006).
193 */
194 printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");
195 }
196
197 if (get_eflags() & X86_EFLAGS_TF) {
198 printf("[FAIL]\tTF is still set\n");
199 nerrs++;
200 }
201
202 if (nerrs) {
203 printf("[FAIL]\tThere were errors\n");
204 return 1;
205 } else {
206 printf("[OK]\tAll is well\n");
207 return 0;
208 }
209}
210
211#endif /* New enough libc */