Re: [PATCH RFC] x86/syscall: Dispatch via sys_call_table when Spectre v2 and BHI are off

From: Peter Zijlstra

Date: Thu Nov 05 2026 - 06:11:42 EST


On Fri, Oct 30, 2026 at 08:56:12AM -1000, Linus Torvalds wrote:
> On Thu, 29 Oct 2026 at 02:30, Breno Leitao <leitao@xxxxxxxxxx> wrote:
> >
> > With "x86_64/entry: Unconditionally use -fno-jump-table", x64_sys_call()
> > becomes a compare tree on every x86_64 build, so system call dispatch is
> > the same whether the mitigations are disabled at boot or at build time,
> > which is basically my end goal.
>
> You're basically saying "with that patch, both cases do equally badly
> whether disabled at boot or build time".
>
> That's not a good end goal. You want both cases to do equally *well*.
>
> I do wonder if we could do computed jumps. It would be lovely if we
> could basically do something like
>
> # syscall number in %rax
> leaq table(%rax,%rax,4),%rax
> jmp *%rax
> table:
> jmp syscall0
> jmp syscall1
> jmp syscall2
> ....
>
> and avoid any indirect memory lookup or indirect branches (outside of
> the one computed one). And no extra call/ret frame.
>
> It's possible that the branch prediction hardware hates those kinds of
> very dense jmp patterns, though. I don't know.
>
> We could have a single static jump that picks between this one and the
> "conditional branch tree" version, as in your patch at the beginning
> of the thread.

The thinking here being that you would still not actually load any value
from the sys_call_table and keep it all in code?

As Andrew already pointed out, this required ENDBR (yes 4 bytes) in
addition to the JMP.d32 (5 bytes). But when building with kCFI that
would require even more.

Notably, you do not want the syscall dispatch to be a kCFI evasion
gadget.

The kCFI landing pad would basically add another 16 bytes on top of
those earlier 9 bytes, at which point you might as well round up and use
32 bytes per entry.

At which point one has to ask the obvious question on whether this is
actually better than just keeping the sys_call_table and syscall
functions as is.


That is; do we want to go back to unconditionally doing:

nr = array_index_nospec(nr, NR_syscalls);
regs->ax = sys_call_table[nr](regs);

Yes, this will turn into retpoline for old hardware, get patched back
into regular indirect call on recent hardware, and will get kCFI/FineIBT
crud on for 'free' when your build swings that way.

It does mean syscall hijack through sys_call_table[] is a thing again,
but it will be the fastest option on modern chips and automagically
'scale' with the mitigation options selected.


OTOH there are still million upon millions of that old stuff out there
in data centers :/