Message ID | 20240411063027.493165-5-andrea.righi@canonical.com |
---|---|
State | New |
Headers | show |
Series | Apply mitigations for the native BHI hardware vulnerabilty | expand |
On 24/04/11 08:24AM, Andrea Righi wrote: > From: Linus Torvalds <torvalds@linux-foundation.org> > > BugLink: https://bugs.launchpad.net/bugs/2060909 > > 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 <torvalds@linux-foundation.org> > Signed-off-by: Daniel Sneddon <daniel.sneddon@linux.intel.com> > Signed-off-by: Thomas Gleixner <tglx@linutronix.de> > Reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org> > (backported from commit 1e3ad78334a69b36e107232e337f9d693dcc9df2) Is this a cherry-pick? I can't spot any changes to 1e3ad78334a69b36e107232e337f9d693dcc9df2. > Signed-off-by: Andrea Righi <andrea.righi@canonical.com> > --- > arch/x86/entry/common.c | 6 +++--- > arch/x86/entry/syscall_32.c | 21 +++++++++++++++++++-- > arch/x86/entry/syscall_64.c | 19 +++++++++++++++++-- > arch/x86/entry/syscall_x32.c | 10 +++++++--- > arch/x86/include/asm/syscall.h | 10 ++++------ > 5 files changed, 50 insertions(+), 16 deletions(-) > > diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c > index 6356060caaf3..cea0e2a23b42 100644 > --- a/arch/x86/entry/common.c > +++ b/arch/x86/entry/common.c > @@ -49,7 +49,7 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr) > > if (likely(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 false; > @@ -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)) { > 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 false; > @@ -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)) { > 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) { > regs->ax = __ia32_sys_ni_syscall(regs); > } > diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c > index 8cfc9bc73e7f..c2235bae17ef 100644 > --- a/arch/x86/entry/syscall_32.c > +++ b/arch/x86/entry/syscall_32.c > @@ -18,8 +18,25 @@ > #include <asm/syscalls_32.h> > #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, > - > -__visible const sys_call_ptr_t ia32_sys_call_table[] = { > +const sys_call_ptr_t sys_call_table[] = { > #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); > + } > +}; > diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c > index be120eec1fc9..33b3f09e6f15 100644 > --- a/arch/x86/entry/syscall_64.c > +++ b/arch/x86/entry/syscall_64.c > @@ -11,8 +11,23 @@ > #include <asm/syscalls_64.h> > #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, > - > -asmlinkage const sys_call_ptr_t sys_call_table[] = { > +const sys_call_ptr_t sys_call_table[] = { > #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); > + } > +}; > diff --git a/arch/x86/entry/syscall_x32.c b/arch/x86/entry/syscall_x32.c > index bdd0e03a1265..03de4a932131 100644 > --- a/arch/x86/entry/syscall_x32.c > +++ b/arch/x86/entry/syscall_x32.c > @@ -11,8 +11,12 @@ > #include <asm/syscalls_x32.h> > #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[] = { > -#include <asm/syscalls_x32.h> > +long x32_sys_call(const struct pt_regs *regs, unsigned int nr) > +{ > + switch (nr) { > + #include <asm/syscalls_x32.h> > + default: return __x64_sys_ni_syscall(regs); > + } > }; > diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h > index f44e2f9ab65d..3c28f26bfe22 100644 > --- a/arch/x86/include/asm/syscall.h > +++ b/arch/x86/include/asm/syscall.h > @@ -16,19 +16,17 @@ > #include <asm/thread_info.h> /* for TS_COMPAT */ > #include <asm/unistd.h> > > +/* This is used purely for kernel/trace/trace_syscalls.c */ > typedef long (*sys_call_ptr_t)(const struct pt_regs *); > 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 > * can use IS_ENABLED(). > */ > -extern const sys_call_ptr_t ia32_sys_call_table[]; > -extern const sys_call_ptr_t x32_sys_call_table[]; > -#endif > +extern long ia32_sys_call(const struct pt_regs *, unsigned int nr); > +extern long x32_sys_call(const struct pt_regs *, unsigned int nr); > +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.
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 6356060caaf3..cea0e2a23b42 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -49,7 +49,7 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr) if (likely(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 false; @@ -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)) { 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 false; @@ -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)) { 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) { regs->ax = __ia32_sys_ni_syscall(regs); } diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c index 8cfc9bc73e7f..c2235bae17ef 100644 --- a/arch/x86/entry/syscall_32.c +++ b/arch/x86/entry/syscall_32.c @@ -18,8 +18,25 @@ #include <asm/syscalls_32.h> #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, - -__visible const sys_call_ptr_t ia32_sys_call_table[] = { +const sys_call_ptr_t sys_call_table[] = { #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); + } +}; diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c index be120eec1fc9..33b3f09e6f15 100644 --- a/arch/x86/entry/syscall_64.c +++ b/arch/x86/entry/syscall_64.c @@ -11,8 +11,23 @@ #include <asm/syscalls_64.h> #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, - -asmlinkage const sys_call_ptr_t sys_call_table[] = { +const sys_call_ptr_t sys_call_table[] = { #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); + } +}; diff --git a/arch/x86/entry/syscall_x32.c b/arch/x86/entry/syscall_x32.c index bdd0e03a1265..03de4a932131 100644 --- a/arch/x86/entry/syscall_x32.c +++ b/arch/x86/entry/syscall_x32.c @@ -11,8 +11,12 @@ #include <asm/syscalls_x32.h> #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[] = { -#include <asm/syscalls_x32.h> +long x32_sys_call(const struct pt_regs *regs, unsigned int nr) +{ + switch (nr) { + #include <asm/syscalls_x32.h> + default: return __x64_sys_ni_syscall(regs); + } }; diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h index f44e2f9ab65d..3c28f26bfe22 100644 --- a/arch/x86/include/asm/syscall.h +++ b/arch/x86/include/asm/syscall.h @@ -16,19 +16,17 @@ #include <asm/thread_info.h> /* for TS_COMPAT */ #include <asm/unistd.h> +/* This is used purely for kernel/trace/trace_syscalls.c */ typedef long (*sys_call_ptr_t)(const struct pt_regs *); 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 * can use IS_ENABLED(). */ -extern const sys_call_ptr_t ia32_sys_call_table[]; -extern const sys_call_ptr_t x32_sys_call_table[]; -#endif +extern long ia32_sys_call(const struct pt_regs *, unsigned int nr); +extern long x32_sys_call(const struct pt_regs *, unsigned int nr); +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.