summaryrefslogtreecommitdiffstats
path: root/arch/x86/include
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2018-04-05 11:53:04 +0200
committerIngo Molnar <mingo@kernel.org>2018-04-05 16:59:38 +0200
commitebeb8c82ffaf94435806ff0b686fffd41dd410b5 (patch)
tree27e14910bb529a9e146949a9ea0563012bca2ea0 /arch/x86/include
parent7303e30ec1d8fb5ca1f07c92d069241c32b2ee1b (diff)
downloadlinux-ebeb8c82ffaf94435806ff0b686fffd41dd410b5.tar.bz2
syscalls/x86: Use 'struct pt_regs' based syscall calling for IA32_EMULATION and x32
Extend ARCH_HAS_SYSCALL_WRAPPER for i386 emulation and for x32 on 64-bit x86. For x32, all we need to do is to create an additional stub for each compat syscall which decodes the parameters in x86-64 ordering, e.g.: asmlinkage long __compat_sys_x32_xyzzy(struct pt_regs *regs) { return c_SyS_xyzzy(regs->di, regs->si, regs->dx); } For i386 emulation, we need to teach compat_sys_*() to take struct pt_regs as its only argument, e.g.: asmlinkage long __compat_sys_ia32_xyzzy(struct pt_regs *regs) { return c_SyS_xyzzy(regs->bx, regs->cx, regs->dx); } In addition, we need to create additional stubs for common syscalls (that is, for syscalls which have the same parameters on 32-bit and 64-bit), e.g.: asmlinkage long __sys_ia32_xyzzy(struct pt_regs *regs) { return c_sys_xyzzy(regs->bx, regs->cx, regs->dx); } This approach avoids leaking random user-provided register content down the call chain. This patch is based on an original proof-of-concept | From: Linus Torvalds <torvalds@linux-foundation.org> | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> and was split up and heavily modified by me, in particular to base it on ARCH_HAS_SYSCALL_WRAPPER. Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/20180405095307.3730-6-linux@dominikbrodowski.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/include')
-rw-r--r--arch/x86/include/asm/syscall_wrapper.h117
1 files changed, 113 insertions, 4 deletions
diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h
index 702bdee377af..49d7e4970110 100644
--- a/arch/x86/include/asm/syscall_wrapper.h
+++ b/arch/x86/include/asm/syscall_wrapper.h
@@ -6,6 +6,111 @@
#ifndef _ASM_X86_SYSCALL_WRAPPER_H
#define _ASM_X86_SYSCALL_WRAPPER_H
+/* Mapping of registers to parameters for syscalls on x86-64 and x32 */
+#define SC_X86_64_REGS_TO_ARGS(x, ...) \
+ __MAP(x,__SC_ARGS \
+ ,,regs->di,,regs->si,,regs->dx \
+ ,,regs->r10,,regs->r8,,regs->r9) \
+
+/* Mapping of registers to parameters for syscalls on i386 */
+#define SC_IA32_REGS_TO_ARGS(x, ...) \
+ __MAP(x,__SC_ARGS \
+ ,,(unsigned int)regs->bx,,(unsigned int)regs->cx \
+ ,,(unsigned int)regs->dx,,(unsigned int)regs->si \
+ ,,(unsigned int)regs->di,,(unsigned int)regs->bp)
+
+#ifdef CONFIG_IA32_EMULATION
+/*
+ * For IA32 emulation, we need to handle "compat" syscalls *and* create
+ * additional wrappers (aptly named __sys_ia32_sys_xyzzy) which decode the
+ * ia32 regs in the proper order for shared or "common" syscalls. As some
+ * syscalls may not be implemented, we need to expand COND_SYSCALL in
+ * kernel/sys_ni.c and SYS_NI in kernel/time/posix-stubs.c to cover this
+ * case as well.
+ */
+#define COMPAT_SC_IA32_STUBx(x, name, ...) \
+ asmlinkage long __compat_sys_ia32##name(const struct pt_regs *regs);\
+ ALLOW_ERROR_INJECTION(__compat_sys_ia32##name, ERRNO); \
+ asmlinkage long __compat_sys_ia32##name(const struct pt_regs *regs)\
+ { \
+ return c_SyS##name(SC_IA32_REGS_TO_ARGS(x,__VA_ARGS__));\
+ } \
+
+#define SC_IA32_WRAPPERx(x, name, ...) \
+ asmlinkage long __sys_ia32##name(const struct pt_regs *regs); \
+ ALLOW_ERROR_INJECTION(__sys_ia32##name, ERRNO); \
+ asmlinkage long __sys_ia32##name(const struct pt_regs *regs) \
+ { \
+ return SyS##name(SC_IA32_REGS_TO_ARGS(x,__VA_ARGS__)); \
+ }
+
+#define COND_SYSCALL(name) \
+ cond_syscall(sys_##name); \
+ cond_syscall(__sys_ia32_##name)
+
+#define SYS_NI(name) \
+ SYSCALL_ALIAS(sys_##name, sys_ni_posix_timers); \
+ SYSCALL_ALIAS(__sys_ia32_##name, sys_ni_posix_timers)
+
+#else /* CONFIG_IA32_EMULATION */
+#define COMPAT_SC_IA32_STUBx(x, name, ...)
+#define SC_IA32_WRAPPERx(x, fullname, name, ...)
+#endif /* CONFIG_IA32_EMULATION */
+
+
+#ifdef CONFIG_X86_X32
+/*
+ * For the x32 ABI, we need to create a stub for compat_sys_*() which is aware
+ * of the x86-64-style parameter ordering of x32 syscalls. The syscalls common
+ * with x86_64 obviously do not need such care.
+ */
+#define COMPAT_SC_X32_STUBx(x, name, ...) \
+ asmlinkage long __compat_sys_x32##name(const struct pt_regs *regs);\
+ ALLOW_ERROR_INJECTION(__compat_sys_x32##name, ERRNO); \
+ asmlinkage long __compat_sys_x32##name(const struct pt_regs *regs)\
+ { \
+ return c_SyS##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
+ } \
+
+#else /* CONFIG_X86_X32 */
+#define COMPAT_SC_X32_STUBx(x, name, ...)
+#endif /* CONFIG_X86_X32 */
+
+
+#ifdef CONFIG_COMPAT
+/*
+ * Compat means IA32_EMULATION and/or X86_X32. As they use a different
+ * mapping of registers to parameters, we need to generate stubs for each
+ * of them. There is no need to implement COMPAT_SYSCALL_DEFINE0, as it is
+ * unused on x86.
+ */
+#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \
+ static long c_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
+ static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+ COMPAT_SC_IA32_STUBx(x, name, __VA_ARGS__) \
+ COMPAT_SC_X32_STUBx(x, name, __VA_ARGS__) \
+ static long c_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
+ { \
+ return C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \
+ } \
+ static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+
+/*
+ * As some compat syscalls may not be implemented, we need to expand
+ * COND_SYSCALL_COMPAT in kernel/sys_ni.c and COMPAT_SYS_NI in
+ * kernel/time/posix-stubs.c to cover this case as well.
+ */
+#define COND_SYSCALL_COMPAT(name) \
+ cond_syscall(__compat_sys_ia32_##name); \
+ cond_syscall(__compat_sys_x32_##name)
+
+#define COMPAT_SYS_NI(name) \
+ SYSCALL_ALIAS(__compat_sys_ia32_##name, sys_ni_posix_timers); \
+ SYSCALL_ALIAS(__compat_sys_x32_##name, sys_ni_posix_timers)
+
+#endif /* CONFIG_COMPAT */
+
+
/*
* Instead of the generic __SYSCALL_DEFINEx() definition, this macro takes
* struct pt_regs *regs as the only argument of the syscall stub named
@@ -34,9 +139,14 @@
* This approach avoids leaking random user-provided register content down
* the call chain.
*
+ * If IA32_EMULATION is enabled, this macro generates an additional wrapper
+ * named __sys_ia32_*() which decodes the struct pt_regs *regs according
+ * to the i386 calling convention (bx, cx, dx, si, di, bp).
+ *
* As the generic SYSCALL_DEFINE0() macro does not decode any parameters for
* obvious reasons, and passing struct pt_regs *regs to it in %rdi does not
- * hurt, there is no need to override it.
+ * hurt, there is no need to override it, or to define it differently for
+ * IA32_EMULATION.
*/
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(const struct pt_regs *regs); \
@@ -45,10 +155,9 @@
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
asmlinkage long sys##name(const struct pt_regs *regs) \
{ \
- return SyS##name(__MAP(x,__SC_ARGS \
- ,,regs->di,,regs->si,,regs->dx \
- ,,regs->r10,,regs->r8,,regs->r9)); \
+ return SyS##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
} \
+ SC_IA32_WRAPPERx(x, name, __VA_ARGS__) \
static long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \
long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \