[PATCH 5.10 488/491] s390/syscalls: Add spectre boundary for syscall dispatch table

Greg Kroah-Hartman
2026-04-13T17:04:04Z

From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

[ Upstream commit 48b8814e25d073dd84daf990a879a820bad2bcbd ]

The s390 syscall number is directly controlled by userspace, but does
not have an array_index_nospec() boundary to prevent access past the
syscall function pointer tables.

Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Fixes: 56e62a737028 ("s390: convert to generic entry")
Cc: stable@kernel.org
Assisted-by: gkh_clanker_2000
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Link: https://lore.kernel.org/r/2026032404-sterling-swoosh-43e6@gregkh
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
[ gor: 5.10 backport. In 5.10, commit 56e62a737028 ("s390: convert to
generic entry") has not been applied — syscall dispatch is in
assembly (entry.S), not in C (syscall.c). The equivalent to
array_index_nospec() is implemented using the same clgr/slbgr/ngr.

SVC 0 path: the user-controlled syscall number in r1 is clamped via
a single unsigned compare (clgr) followed by slbgr/ngr. The original
cghi/jnl bounds check branch is replaced — the clamp handles both
cases: in-bounds values pass through, out-of-bounds values are zeroed
(producing the same r8=0 dispatch to table[0] as the original branch).

SVC 1-255 path: syscall number from the 8-bit instruction immediate
is always in bounds. ]
Reviewed-by: Sven Schnelle <svens@linux.ibm.com>
Reviewed-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
arch/s390/kernel/entry.S | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -420,12 +420,15 @@ ENTRY(system_call)
# svc 0: system call number in %r1
llgfr %r1,%r1 # clear high word in r1
sth %r1,__PT_INT_CODE+2(%r11)
- cghi %r1,NR_syscalls
- jnl .Lsysc_nr_ok
+ lghi %r0,NR_syscalls-1
+ clgr %r1,%r0 # CC0/1 if r1 in bounds
+ slbgr %r0,%r0 # mask = -1 in bounds, 0 out of bounds
+ ngr %r1,%r0 # clamp r1
slag %r8,%r1,3
.Lsysc_nr_ok:
stg %r2,__PT_ORIG_GPR2(%r11)
stg %r7,STACK_FRAME_OVERHEAD(%r15)
+ xgr %r1,%r1 # scrub r1, unclamped user value for svc 1-255
lg %r9,0(%r8,%r10) # get system call add.
TSTMSK __TI_flags(%r12),_TIF_TRACE
jnz .Lsysc_tracesys