Commit 1e3ad783 authored by Linus Torvalds's avatar Linus Torvalds Committed by Thomas Gleixner

x86/syscall: Don't force use of indirect calls for system calls

Make <asm/syscall.h> build a switch statement instead, and the compiler can
either decide to generate an indirect jump, or - more likely these days due
to mitigations - just a series of conditional branches.

Yes, the conditional branches also have branch prediction, but the branch
prediction is much more controlled, in that it just causes speculatively
running the wrong system call (harmless), rather than speculatively running
possibly wrong random less controlled code gadgets.

This doesn't mitigate other indirect calls, but the system call indirection
is the first and most easily triggered case.
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarDaniel Sneddon <daniel.sneddon@linux.intel.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
parent 0cd01ac5
...@@ -49,7 +49,7 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr) ...@@ -49,7 +49,7 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
if (likely(unr < NR_syscalls)) { if (likely(unr < NR_syscalls)) {
unr = array_index_nospec(unr, NR_syscalls); unr = array_index_nospec(unr, NR_syscalls);
regs->ax = sys_call_table[unr](regs); regs->ax = x64_sys_call(regs, unr);
return true; return true;
} }
return false; return false;
...@@ -66,7 +66,7 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr) ...@@ -66,7 +66,7 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) { if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
xnr = array_index_nospec(xnr, X32_NR_syscalls); xnr = array_index_nospec(xnr, X32_NR_syscalls);
regs->ax = x32_sys_call_table[xnr](regs); regs->ax = x32_sys_call(regs, xnr);
return true; return true;
} }
return false; return false;
...@@ -162,7 +162,7 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr) ...@@ -162,7 +162,7 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr)
if (likely(unr < IA32_NR_syscalls)) { if (likely(unr < IA32_NR_syscalls)) {
unr = array_index_nospec(unr, IA32_NR_syscalls); unr = array_index_nospec(unr, IA32_NR_syscalls);
regs->ax = ia32_sys_call_table[unr](regs); regs->ax = ia32_sys_call(regs, unr);
} else if (nr != -1) { } else if (nr != -1) {
regs->ax = __ia32_sys_ni_syscall(regs); regs->ax = __ia32_sys_ni_syscall(regs);
} }
......
...@@ -18,8 +18,25 @@ ...@@ -18,8 +18,25 @@
#include <asm/syscalls_32.h> #include <asm/syscalls_32.h>
#undef __SYSCALL #undef __SYSCALL
/*
* The sys_call_table[] is no longer used for system calls, but
* kernel/trace/trace_syscalls.c still wants to know the system
* call address.
*/
#ifdef CONFIG_X86_32
#define __SYSCALL(nr, sym) __ia32_##sym, #define __SYSCALL(nr, sym) __ia32_##sym,
const sys_call_ptr_t sys_call_table[] = {
__visible const sys_call_ptr_t ia32_sys_call_table[] = {
#include <asm/syscalls_32.h> #include <asm/syscalls_32.h>
}; };
#undef __SYSCALL
#endif
#define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs);
long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
{
switch (nr) {
#include <asm/syscalls_32.h>
default: return __ia32_sys_ni_syscall(regs);
}
};
...@@ -11,8 +11,23 @@ ...@@ -11,8 +11,23 @@
#include <asm/syscalls_64.h> #include <asm/syscalls_64.h>
#undef __SYSCALL #undef __SYSCALL
/*
* The sys_call_table[] is no longer used for system calls, but
* kernel/trace/trace_syscalls.c still wants to know the system
* call address.
*/
#define __SYSCALL(nr, sym) __x64_##sym, #define __SYSCALL(nr, sym) __x64_##sym,
const sys_call_ptr_t sys_call_table[] = {
asmlinkage const sys_call_ptr_t sys_call_table[] = {
#include <asm/syscalls_64.h> #include <asm/syscalls_64.h>
}; };
#undef __SYSCALL
#define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
{
switch (nr) {
#include <asm/syscalls_64.h>
default: return __x64_sys_ni_syscall(regs);
}
};
...@@ -11,8 +11,12 @@ ...@@ -11,8 +11,12 @@
#include <asm/syscalls_x32.h> #include <asm/syscalls_x32.h>
#undef __SYSCALL #undef __SYSCALL
#define __SYSCALL(nr, sym) __x64_##sym, #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
asmlinkage const sys_call_ptr_t x32_sys_call_table[] = { long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
#include <asm/syscalls_x32.h> {
switch (nr) {
#include <asm/syscalls_x32.h>
default: return __x64_sys_ni_syscall(regs);
}
}; };
...@@ -16,19 +16,17 @@ ...@@ -16,19 +16,17 @@
#include <asm/thread_info.h> /* for TS_COMPAT */ #include <asm/thread_info.h> /* for TS_COMPAT */
#include <asm/unistd.h> #include <asm/unistd.h>
/* This is used purely for kernel/trace/trace_syscalls.c */
typedef long (*sys_call_ptr_t)(const struct pt_regs *); typedef long (*sys_call_ptr_t)(const struct pt_regs *);
extern const sys_call_ptr_t sys_call_table[]; extern const sys_call_ptr_t sys_call_table[];
#if defined(CONFIG_X86_32)
#define ia32_sys_call_table sys_call_table
#else
/* /*
* These may not exist, but still put the prototypes in so we * These may not exist, but still put the prototypes in so we
* can use IS_ENABLED(). * can use IS_ENABLED().
*/ */
extern const sys_call_ptr_t ia32_sys_call_table[]; extern long ia32_sys_call(const struct pt_regs *, unsigned int nr);
extern const sys_call_ptr_t x32_sys_call_table[]; extern long x32_sys_call(const struct pt_regs *, unsigned int nr);
#endif extern long x64_sys_call(const struct pt_regs *, unsigned int nr);
/* /*
* Only the low 32 bits of orig_ax are meaningful, so we return int. * Only the low 32 bits of orig_ax are meaningful, so we return int.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment