aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/Kconfig5
-rw-r--r--arch/x86/entry/common.c4
-rw-r--r--arch/x86/entry/syscall_64.c9
-rw-r--r--arch/x86/entry/vsyscall/vsyscall_64.c22
-rw-r--r--arch/x86/include/asm/syscall.h4
-rw-r--r--arch/x86/include/asm/syscall_wrapper.h70
-rw-r--r--arch/x86/include/asm/syscalls.h7
-rw-r--r--include/linux/syscalls.h2
8 files changed, 120 insertions, 3 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 27fede438959..67348efc2540 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2954,3 +2954,8 @@ source "crypto/Kconfig"
2954source "arch/x86/kvm/Kconfig" 2954source "arch/x86/kvm/Kconfig"
2955 2955
2956source "lib/Kconfig" 2956source "lib/Kconfig"
2957
2958config SYSCALL_PTREGS
2959 def_bool y
2960 depends on X86_64 && !COMPAT
2961 select ARCH_HAS_SYSCALL_WRAPPER
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index a8b066dbbf48..e1b91bffa988 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -284,9 +284,13 @@ __visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
284 nr &= __SYSCALL_MASK; 284 nr &= __SYSCALL_MASK;
285 if (likely(nr < NR_syscalls)) { 285 if (likely(nr < NR_syscalls)) {
286 nr = array_index_nospec(nr, NR_syscalls); 286 nr = array_index_nospec(nr, NR_syscalls);
287#ifdef CONFIG_SYSCALL_PTREGS
288 regs->ax = sys_call_table[nr](regs);
289#else
287 regs->ax = sys_call_table[nr]( 290 regs->ax = sys_call_table[nr](
288 regs->di, regs->si, regs->dx, 291 regs->di, regs->si, regs->dx,
289 regs->r10, regs->r8, regs->r9); 292 regs->r10, regs->r8, regs->r9);
293#endif
290 } 294 }
291 295
292 syscall_return_slowpath(regs); 296 syscall_return_slowpath(regs);
diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
index c176d2fab1da..6197850adf91 100644
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -7,14 +7,19 @@
7#include <asm/asm-offsets.h> 7#include <asm/asm-offsets.h>
8#include <asm/syscall.h> 8#include <asm/syscall.h>
9 9
10#ifdef CONFIG_SYSCALL_PTREGS
11/* this is a lie, but it does not hurt as sys_ni_syscall just returns -EINVAL */
12extern asmlinkage long sys_ni_syscall(const struct pt_regs *);
13#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
14#else /* CONFIG_SYSCALL_PTREGS */
15extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
10#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); 16#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
17#endif /* CONFIG_SYSCALL_PTREGS */
11#include <asm/syscalls_64.h> 18#include <asm/syscalls_64.h>
12#undef __SYSCALL_64 19#undef __SYSCALL_64
13 20
14#define __SYSCALL_64(nr, sym, qual) [nr] = sym, 21#define __SYSCALL_64(nr, sym, qual) [nr] = sym,
15 22
16extern long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
17
18asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { 23asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
19 /* 24 /*
20 * Smells like a compiler bug -- it doesn't work 25 * Smells like a compiler bug -- it doesn't work
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
index 317be365bce3..05eebbf9b989 100644
--- a/arch/x86/entry/vsyscall/vsyscall_64.c
+++ b/arch/x86/entry/vsyscall/vsyscall_64.c
@@ -127,6 +127,9 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
127 int vsyscall_nr, syscall_nr, tmp; 127 int vsyscall_nr, syscall_nr, tmp;
128 int prev_sig_on_uaccess_err; 128 int prev_sig_on_uaccess_err;
129 long ret; 129 long ret;
130#ifdef CONFIG_SYSCALL_PTREGS
131 unsigned long orig_dx;
132#endif
130 133
131 /* 134 /*
132 * No point in checking CS -- the only way to get here is a user mode 135 * No point in checking CS -- the only way to get here is a user mode
@@ -227,19 +230,38 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
227 ret = -EFAULT; 230 ret = -EFAULT;
228 switch (vsyscall_nr) { 231 switch (vsyscall_nr) {
229 case 0: 232 case 0:
233#ifdef CONFIG_SYSCALL_PTREGS
234 /* this decodes regs->di and regs->si on its own */
235 ret = sys_gettimeofday(regs);
236#else
230 ret = sys_gettimeofday( 237 ret = sys_gettimeofday(
231 (struct timeval __user *)regs->di, 238 (struct timeval __user *)regs->di,
232 (struct timezone __user *)regs->si); 239 (struct timezone __user *)regs->si);
240#endif /* CONFIG_SYSCALL_PTREGS */
233 break; 241 break;
234 242
235 case 1: 243 case 1:
244#ifdef CONFIG_SYSCALL_PTREGS
245 /* this decodes regs->di on its own */
246 ret = sys_time(regs);
247#else
236 ret = sys_time((time_t __user *)regs->di); 248 ret = sys_time((time_t __user *)regs->di);
249#endif /* CONFIG_SYSCALL_PTREGS */
237 break; 250 break;
238 251
239 case 2: 252 case 2:
253#ifdef CONFIG_SYSCALL_PTREGS
254 /* while we could clobber regs->dx, we didn't in the past... */
255 orig_dx = regs->dx;
256 regs->dx = 0;
257 /* this decodes regs->di, regs->si and regs->dx on its own */
258 ret = sys_getcpu(regs);
259 regs->dx = orig_dx;
260#else
240 ret = sys_getcpu((unsigned __user *)regs->di, 261 ret = sys_getcpu((unsigned __user *)regs->di,
241 (unsigned __user *)regs->si, 262 (unsigned __user *)regs->si,
242 NULL); 263 NULL);
264#endif /* CONFIG_SYSCALL_PTREGS */
243 break; 265 break;
244 } 266 }
245 267
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index 03eedc21246d..17c62373a6f9 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -20,9 +20,13 @@
20#include <asm/thread_info.h> /* for TS_COMPAT */ 20#include <asm/thread_info.h> /* for TS_COMPAT */
21#include <asm/unistd.h> 21#include <asm/unistd.h>
22 22
23#ifdef CONFIG_SYSCALL_PTREGS
24typedef asmlinkage long (*sys_call_ptr_t)(const struct pt_regs *);
25#else
23typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long, 26typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
24 unsigned long, unsigned long, 27 unsigned long, unsigned long,
25 unsigned long, unsigned long); 28 unsigned long, unsigned long);
29#endif /* CONFIG_SYSCALL_PTREGS */
26extern const sys_call_ptr_t sys_call_table[]; 30extern const sys_call_ptr_t sys_call_table[];
27 31
28#if defined(CONFIG_X86_32) 32#if defined(CONFIG_X86_32)
diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h
new file mode 100644
index 000000000000..702bdee377af
--- /dev/null
+++ b/arch/x86/include/asm/syscall_wrapper.h
@@ -0,0 +1,70 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * syscall_wrapper.h - x86 specific wrappers to syscall definitions
4 */
5
6#ifndef _ASM_X86_SYSCALL_WRAPPER_H
7#define _ASM_X86_SYSCALL_WRAPPER_H
8
9/*
10 * Instead of the generic __SYSCALL_DEFINEx() definition, this macro takes
11 * struct pt_regs *regs as the only argument of the syscall stub named
12 * sys_*(). It decodes just the registers it needs and passes them on to
13 * the SyS_*() wrapper and then to the SYSC_*() function doing the actual job.
14 * These wrappers and functions are inlined, meaning that the assembly looks
15 * as follows (slightly re-ordered):
16 *
17 * <sys_recv>: <-- syscall with 4 parameters
18 * callq <__fentry__>
19 *
20 * mov 0x70(%rdi),%rdi <-- decode regs->di
21 * mov 0x68(%rdi),%rsi <-- decode regs->si
22 * mov 0x60(%rdi),%rdx <-- decode regs->dx
23 * mov 0x38(%rdi),%rcx <-- decode regs->r10
24 *
25 * xor %r9d,%r9d <-- clear %r9
26 * xor %r8d,%r8d <-- clear %r8
27 *
28 * callq __sys_recvfrom <-- do the actual work in __sys_recvfrom()
29 * which takes 6 arguments
30 *
31 * cltq <-- extend return value to 64-bit
32 * retq <-- return
33 *
34 * This approach avoids leaking random user-provided register content down
35 * the call chain.
36 *
37 * As the generic SYSCALL_DEFINE0() macro does not decode any parameters for
38 * obvious reasons, and passing struct pt_regs *regs to it in %rdi does not
39 * hurt, there is no need to override it.
40 */
41#define __SYSCALL_DEFINEx(x, name, ...) \
42 asmlinkage long sys##name(const struct pt_regs *regs); \
43 ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
44 static long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
45 static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
46 asmlinkage long sys##name(const struct pt_regs *regs) \
47 { \
48 return SyS##name(__MAP(x,__SC_ARGS \
49 ,,regs->di,,regs->si,,regs->dx \
50 ,,regs->r10,,regs->r8,,regs->r9)); \
51 } \
52 static long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
53 { \
54 long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \
55 __MAP(x,__SC_TEST,__VA_ARGS__); \
56 __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
57 return ret; \
58 } \
59 static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
60
61/*
62 * For VSYSCALLS, we need to declare these three syscalls with the new
63 * pt_regs-based calling convention for in-kernel use.
64 */
65struct pt_regs;
66asmlinkage long sys_getcpu(const struct pt_regs *regs); /* di,si,dx */
67asmlinkage long sys_gettimeofday(const struct pt_regs *regs); /* di,si */
68asmlinkage long sys_time(const struct pt_regs *regs); /* di */
69
70#endif /* _ASM_X86_SYSCALL_WRAPPER_H */
diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h
index ae6e05fdc24b..e4ad93c05f02 100644
--- a/arch/x86/include/asm/syscalls.h
+++ b/arch/x86/include/asm/syscalls.h
@@ -18,6 +18,12 @@
18/* Common in X86_32 and X86_64 */ 18/* Common in X86_32 and X86_64 */
19/* kernel/ioport.c */ 19/* kernel/ioport.c */
20long ksys_ioperm(unsigned long from, unsigned long num, int turn_on); 20long ksys_ioperm(unsigned long from, unsigned long num, int turn_on);
21
22#ifndef CONFIG_SYSCALL_PTREGS
23/*
24 * If CONFIG_SYSCALL_PTREGS is enabled, a different syscall calling convention
25 * is used. Do not include these -- invalid -- prototypes then
26 */
21asmlinkage long sys_ioperm(unsigned long, unsigned long, int); 27asmlinkage long sys_ioperm(unsigned long, unsigned long, int);
22asmlinkage long sys_iopl(unsigned int); 28asmlinkage long sys_iopl(unsigned int);
23 29
@@ -53,4 +59,5 @@ asmlinkage long sys_mmap(unsigned long, unsigned long, unsigned long,
53 unsigned long, unsigned long, unsigned long); 59 unsigned long, unsigned long, unsigned long);
54 60
55#endif /* CONFIG_X86_32 */ 61#endif /* CONFIG_X86_32 */
62#endif /* CONFIG_SYSCALL_PTREGS */
56#endif /* _ASM_X86_SYSCALLS_H */ 63#endif /* _ASM_X86_SYSCALLS_H */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 503ab245d4ce..d7168b3a4b4c 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -102,7 +102,7 @@ union bpf_attr;
102 * for SYSCALL_DEFINE<n>/COMPAT_SYSCALL_DEFINE<n> 102 * for SYSCALL_DEFINE<n>/COMPAT_SYSCALL_DEFINE<n>
103 */ 103 */
104#define __MAP0(m,...) 104#define __MAP0(m,...)
105#define __MAP1(m,t,a) m(t,a) 105#define __MAP1(m,t,a,...) m(t,a)
106#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__) 106#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
107#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__) 107#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
108#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__) 108#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)