Skip to content

Commit 1e3ad78

Browse files
torvaldsKAGA-KOKO
authored andcommitted
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: Linus Torvalds <[email protected]> Signed-off-by: Daniel Sneddon <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Josh Poimboeuf <[email protected]>
1 parent 0cd01ac commit 1e3ad78

File tree

5 files changed

+50
-16
lines changed

5 files changed

+50
-16
lines changed

arch/x86/entry/common.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
4949

5050
if (likely(unr < NR_syscalls)) {
5151
unr = array_index_nospec(unr, NR_syscalls);
52-
regs->ax = sys_call_table[unr](regs);
52+
regs->ax = x64_sys_call(regs, unr);
5353
return true;
5454
}
5555
return false;
@@ -66,7 +66,7 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
6666

6767
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
6868
xnr = array_index_nospec(xnr, X32_NR_syscalls);
69-
regs->ax = x32_sys_call_table[xnr](regs);
69+
regs->ax = x32_sys_call(regs, xnr);
7070
return true;
7171
}
7272
return false;
@@ -162,7 +162,7 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr)
162162

163163
if (likely(unr < IA32_NR_syscalls)) {
164164
unr = array_index_nospec(unr, IA32_NR_syscalls);
165-
regs->ax = ia32_sys_call_table[unr](regs);
165+
regs->ax = ia32_sys_call(regs, unr);
166166
} else if (nr != -1) {
167167
regs->ax = __ia32_sys_ni_syscall(regs);
168168
}

arch/x86/entry/syscall_32.c

+19-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,25 @@
1818
#include <asm/syscalls_32.h>
1919
#undef __SYSCALL
2020

21+
/*
22+
* The sys_call_table[] is no longer used for system calls, but
23+
* kernel/trace/trace_syscalls.c still wants to know the system
24+
* call address.
25+
*/
26+
#ifdef CONFIG_X86_32
2127
#define __SYSCALL(nr, sym) __ia32_##sym,
22-
23-
__visible const sys_call_ptr_t ia32_sys_call_table[] = {
28+
const sys_call_ptr_t sys_call_table[] = {
2429
#include <asm/syscalls_32.h>
2530
};
31+
#undef __SYSCALL
32+
#endif
33+
34+
#define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs);
35+
36+
long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
37+
{
38+
switch (nr) {
39+
#include <asm/syscalls_32.h>
40+
default: return __ia32_sys_ni_syscall(regs);
41+
}
42+
};

arch/x86/entry/syscall_64.c

+17-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,23 @@
1111
#include <asm/syscalls_64.h>
1212
#undef __SYSCALL
1313

14+
/*
15+
* The sys_call_table[] is no longer used for system calls, but
16+
* kernel/trace/trace_syscalls.c still wants to know the system
17+
* call address.
18+
*/
1419
#define __SYSCALL(nr, sym) __x64_##sym,
15-
16-
asmlinkage const sys_call_ptr_t sys_call_table[] = {
20+
const sys_call_ptr_t sys_call_table[] = {
1721
#include <asm/syscalls_64.h>
1822
};
23+
#undef __SYSCALL
24+
25+
#define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
26+
27+
long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
28+
{
29+
switch (nr) {
30+
#include <asm/syscalls_64.h>
31+
default: return __x64_sys_ni_syscall(regs);
32+
}
33+
};

arch/x86/entry/syscall_x32.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
#include <asm/syscalls_x32.h>
1212
#undef __SYSCALL
1313

14-
#define __SYSCALL(nr, sym) __x64_##sym,
14+
#define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
1515

16-
asmlinkage const sys_call_ptr_t x32_sys_call_table[] = {
17-
#include <asm/syscalls_x32.h>
16+
long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
17+
{
18+
switch (nr) {
19+
#include <asm/syscalls_x32.h>
20+
default: return __x64_sys_ni_syscall(regs);
21+
}
1822
};

arch/x86/include/asm/syscall.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,17 @@
1616
#include <asm/thread_info.h> /* for TS_COMPAT */
1717
#include <asm/unistd.h>
1818

19+
/* This is used purely for kernel/trace/trace_syscalls.c */
1920
typedef long (*sys_call_ptr_t)(const struct pt_regs *);
2021
extern const sys_call_ptr_t sys_call_table[];
2122

22-
#if defined(CONFIG_X86_32)
23-
#define ia32_sys_call_table sys_call_table
24-
#else
2523
/*
2624
* These may not exist, but still put the prototypes in so we
2725
* can use IS_ENABLED().
2826
*/
29-
extern const sys_call_ptr_t ia32_sys_call_table[];
30-
extern const sys_call_ptr_t x32_sys_call_table[];
31-
#endif
27+
extern long ia32_sys_call(const struct pt_regs *, unsigned int nr);
28+
extern long x32_sys_call(const struct pt_regs *, unsigned int nr);
29+
extern long x64_sys_call(const struct pt_regs *, unsigned int nr);
3230

3331
/*
3432
* Only the low 32 bits of orig_ax are meaningful, so we return int.

0 commit comments

Comments
 (0)