Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ebeb8c82 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Ingo Molnar
Browse files

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: default avatarLinus 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: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Acked-by: default avatarLinus 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: default avatarIngo Molnar <mingo@kernel.org>
parent 7303e30e
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -2957,5 +2957,5 @@ source "lib/Kconfig"


config SYSCALL_PTREGS
config SYSCALL_PTREGS
	def_bool y
	def_bool y
	depends on X86_64 && !COMPAT
	depends on X86_64
	select ARCH_HAS_SYSCALL_WRAPPER
	select ARCH_HAS_SYSCALL_WRAPPER
+4 −0
Original line number Original line Diff line number Diff line
@@ -325,6 +325,9 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs)


	if (likely(nr < IA32_NR_syscalls)) {
	if (likely(nr < IA32_NR_syscalls)) {
		nr = array_index_nospec(nr, IA32_NR_syscalls);
		nr = array_index_nospec(nr, IA32_NR_syscalls);
#ifdef CONFIG_SYSCALL_PTREGS
		regs->ax = ia32_sys_call_table[nr](regs);
#else
		/*
		/*
		 * It's possible that a 32-bit syscall implementation
		 * It's possible that a 32-bit syscall implementation
		 * takes a 64-bit parameter but nonetheless assumes that
		 * 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)
			(unsigned int)regs->bx, (unsigned int)regs->cx,
			(unsigned int)regs->bx, (unsigned int)regs->cx,
			(unsigned int)regs->dx, (unsigned int)regs->si,
			(unsigned int)regs->dx, (unsigned int)regs->si,
			(unsigned int)regs->di, (unsigned int)regs->bp);
			(unsigned int)regs->di, (unsigned int)regs->bp);
#endif /* CONFIG_SYSCALL_PTREGS */
	}
	}


	syscall_return_slowpath(regs);
	syscall_return_slowpath(regs);
+12 −3
Original line number Original line Diff line number Diff line
@@ -7,14 +7,23 @@
#include <asm/asm-offsets.h>
#include <asm/asm-offsets.h>
#include <asm/syscall.h>
#include <asm/syscall.h>


#ifdef CONFIG_SYSCALL_PTREGS
/* On X86_64, we use struct pt_regs * to pass parameters to syscalls */
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);

/* this is a lie, but it does not hurt as sys_ni_syscall just returns -EINVAL */
extern asmlinkage long sys_ni_syscall(const struct pt_regs *);

#else /* CONFIG_SYSCALL_PTREGS */
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#define __SYSCALL_I386(nr, sym, qual) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#endif /* CONFIG_SYSCALL_PTREGS */

#include <asm/syscalls_32.h>
#include <asm/syscalls_32.h>
#undef __SYSCALL_I386
#undef __SYSCALL_I386


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


extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);

__visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
__visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
	/*
	/*
	 * Smells like a compiler bug -- it doesn't work
	 * Smells like a compiler bug -- it doesn't work
Loading