Skip to content

Commit ebeb8c8

Browse files
Dominik Brodowskiingomolnar
authored andcommitted
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>
1 parent 7303e30 commit ebeb8c8

6 files changed

Lines changed: 509 additions & 380 deletions

File tree

arch/x86/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2957,5 +2957,5 @@ source "lib/Kconfig"
29572957

29582958
config SYSCALL_PTREGS
29592959
def_bool y
2960-
depends on X86_64 && !COMPAT
2960+
depends on X86_64
29612961
select ARCH_HAS_SYSCALL_WRAPPER

arch/x86/entry/common.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs)
325325

326326
if (likely(nr < IA32_NR_syscalls)) {
327327
nr = array_index_nospec(nr, IA32_NR_syscalls);
328+
#ifdef CONFIG_SYSCALL_PTREGS
329+
regs->ax = ia32_sys_call_table[nr](regs);
330+
#else
328331
/*
329332
* It's possible that a 32-bit syscall implementation
330333
* takes a 64-bit parameter but nonetheless assumes that
@@ -335,6 +338,7 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs)
335338
(unsigned int)regs->bx, (unsigned int)regs->cx,
336339
(unsigned int)regs->dx, (unsigned int)regs->si,
337340
(unsigned int)regs->di, (unsigned int)regs->bp);
341+
#endif /* CONFIG_SYSCALL_PTREGS */
338342
}
339343

340344
syscall_return_slowpath(regs);

arch/x86/entry/syscall_32.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,23 @@
77
#include <asm/asm-offsets.h>
88
#include <asm/syscall.h>
99

10-
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) ;
10+
#ifdef CONFIG_SYSCALL_PTREGS
11+
/* On X86_64, we use struct pt_regs * to pass parameters to syscalls */
12+
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
13+
14+
/* this is a lie, but it does not hurt as sys_ni_syscall just returns -EINVAL */
15+
extern asmlinkage long sys_ni_syscall(const struct pt_regs *);
16+
17+
#else /* CONFIG_SYSCALL_PTREGS */
18+
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
19+
extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
20+
#endif /* CONFIG_SYSCALL_PTREGS */
21+
1122
#include <asm/syscalls_32.h>
1223
#undef __SYSCALL_I386
1324

1425
#define __SYSCALL_I386(nr, sym, qual) [nr] = sym,
1526

16-
extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
17-
1827
__visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
1928
/*
2029
* Smells like a compiler bug -- it doesn't work

0 commit comments

Comments
 (0)