Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/syscall: Mark exit[_group] syscall handlers __noreturn

The direct-call syscall dispatch function doesn't know that the exit()
and exit_group() syscall handlers don't return, so the call sites aren't
optimized accordingly.

Fix that by marking the exit syscall declarations __noreturn.

Fixes the following warnings:

vmlinux.o: warning: objtool: x64_sys_call+0x2804: __x64_sys_exit() is missing a __noreturn annotation
vmlinux.o: warning: objtool: ia32_sys_call+0x29b6: __ia32_sys_exit_group() is missing a __noreturn annotation

Fixes: 1e3ad78334a6 ("x86/syscall: Don't force use of indirect calls for system calls")
Closes: https://lkml.kernel.org/lkml/6dba9b32-db2c-4e6d-9500-7a08852f17a3@paulmck-laptop
Reported-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Tested-by: Paul E. McKenney <paulmck@kernel.org>
Link: https://lore.kernel.org/r/5d8882bc077d8eadcc7fd1740b56dfb781f12288.1719381528.git.jpoimboe@kernel.org

authored by

Josh Poimboeuf and committed by
Borislav Petkov (AMD)
9142be9e f2661062

+56 -25
+6 -4
arch/x86/entry/syscall_32.c
··· 14 14 #endif 15 15 16 16 #define __SYSCALL(nr, sym) extern long __ia32_##sym(const struct pt_regs *); 17 - 17 + #define __SYSCALL_NORETURN(nr, sym) extern long __noreturn __ia32_##sym(const struct pt_regs *); 18 18 #include <asm/syscalls_32.h> 19 - #undef __SYSCALL 19 + #undef __SYSCALL 20 + 21 + #undef __SYSCALL_NORETURN 22 + #define __SYSCALL_NORETURN __SYSCALL 20 23 21 24 /* 22 25 * The sys_call_table[] is no longer used for system calls, but ··· 31 28 const sys_call_ptr_t sys_call_table[] = { 32 29 #include <asm/syscalls_32.h> 33 30 }; 34 - #undef __SYSCALL 31 + #undef __SYSCALL 35 32 #endif 36 33 37 34 #define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs); 38 - 39 35 long ia32_sys_call(const struct pt_regs *regs, unsigned int nr) 40 36 { 41 37 switch (nr) {
+6 -3
arch/x86/entry/syscall_64.c
··· 8 8 #include <asm/syscall.h> 9 9 10 10 #define __SYSCALL(nr, sym) extern long __x64_##sym(const struct pt_regs *); 11 + #define __SYSCALL_NORETURN(nr, sym) extern long __noreturn __x64_##sym(const struct pt_regs *); 11 12 #include <asm/syscalls_64.h> 12 - #undef __SYSCALL 13 + #undef __SYSCALL 14 + 15 + #undef __SYSCALL_NORETURN 16 + #define __SYSCALL_NORETURN __SYSCALL 13 17 14 18 /* 15 19 * The sys_call_table[] is no longer used for system calls, but ··· 24 20 const sys_call_ptr_t sys_call_table[] = { 25 21 #include <asm/syscalls_64.h> 26 22 }; 27 - #undef __SYSCALL 23 + #undef __SYSCALL 28 24 29 25 #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs); 30 - 31 26 long x64_sys_call(const struct pt_regs *regs, unsigned int nr) 32 27 { 33 28 switch (nr) {
+5 -2
arch/x86/entry/syscall_x32.c
··· 8 8 #include <asm/syscall.h> 9 9 10 10 #define __SYSCALL(nr, sym) extern long __x64_##sym(const struct pt_regs *); 11 + #define __SYSCALL_NORETURN(nr, sym) extern long __noreturn __x64_##sym(const struct pt_regs *); 11 12 #include <asm/syscalls_x32.h> 12 - #undef __SYSCALL 13 + #undef __SYSCALL 14 + 15 + #undef __SYSCALL_NORETURN 16 + #define __SYSCALL_NORETURN __SYSCALL 13 17 14 18 #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs); 15 - 16 19 long x32_sys_call(const struct pt_regs *regs, unsigned int nr) 17 20 { 18 21 switch (nr) {
+3 -3
arch/x86/entry/syscalls/syscall_32.tbl
··· 2 2 # 32-bit system call numbers and entry vectors 3 3 # 4 4 # The format is: 5 - # <number> <abi> <name> <entry point> <compat entry point> 5 + # <number> <abi> <name> <entry point> [<compat entry point> [noreturn]] 6 6 # 7 7 # The __ia32_sys and __ia32_compat_sys stubs are created on-the-fly for 8 8 # sys_*() system calls and compat_sys_*() compat system calls if ··· 12 12 # The abi is always "i386" for this file. 13 13 # 14 14 0 i386 restart_syscall sys_restart_syscall 15 - 1 i386 exit sys_exit 15 + 1 i386 exit sys_exit - noreturn 16 16 2 i386 fork sys_fork 17 17 3 i386 read sys_read 18 18 4 i386 write sys_write ··· 263 263 249 i386 io_cancel sys_io_cancel 264 264 250 i386 fadvise64 sys_ia32_fadvise64 265 265 # 251 is available for reuse (was briefly sys_set_zone_reclaim) 266 - 252 i386 exit_group sys_exit_group 266 + 252 i386 exit_group sys_exit_group - noreturn 267 267 253 i386 lookup_dcookie 268 268 254 i386 epoll_create sys_epoll_create 269 269 255 i386 epoll_ctl sys_epoll_ctl
+3 -3
arch/x86/entry/syscalls/syscall_64.tbl
··· 2 2 # 64-bit system call numbers and entry vectors 3 3 # 4 4 # The format is: 5 - # <number> <abi> <name> <entry point> 5 + # <number> <abi> <name> <entry point> [<compat entry point> [noreturn]] 6 6 # 7 7 # The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls 8 8 # ··· 68 68 57 common fork sys_fork 69 69 58 common vfork sys_vfork 70 70 59 64 execve sys_execve 71 - 60 common exit sys_exit 71 + 60 common exit sys_exit - noreturn 72 72 61 common wait4 sys_wait4 73 73 62 common kill sys_kill 74 74 63 common uname sys_newuname ··· 239 239 228 common clock_gettime sys_clock_gettime 240 240 229 common clock_getres sys_clock_getres 241 241 230 common clock_nanosleep sys_clock_nanosleep 242 - 231 common exit_group sys_exit_group 242 + 231 common exit_group sys_exit_group - noreturn 243 243 232 common epoll_wait sys_epoll_wait 244 244 233 common epoll_ctl sys_epoll_ctl 245 245 234 common tgkill sys_tgkill
+6 -4
arch/x86/um/sys_call_table_32.c
··· 9 9 #include <linux/cache.h> 10 10 #include <asm/syscall.h> 11 11 12 + extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, 13 + unsigned long, unsigned long, 14 + unsigned long, unsigned long); 15 + 12 16 /* 13 17 * Below you can see, in terms of #define's, the differences between the x86-64 14 18 * and the UML syscall table. ··· 26 22 #define sys_vm86 sys_ni_syscall 27 23 28 24 #define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native) 25 + #define __SYSCALL_NORETURN __SYSCALL 29 26 30 27 #define __SYSCALL(nr, sym) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); 31 28 #include <asm/syscalls_32.h> 29 + #undef __SYSCALL 32 30 33 - #undef __SYSCALL 34 31 #define __SYSCALL(nr, sym) sym, 35 - 36 - extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); 37 - 38 32 const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = { 39 33 #include <asm/syscalls_32.h> 40 34 };
+7 -4
arch/x86/um/sys_call_table_64.c
··· 9 9 #include <linux/cache.h> 10 10 #include <asm/syscall.h> 11 11 12 + extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, 13 + unsigned long, unsigned long, 14 + unsigned long, unsigned long); 15 + 12 16 /* 13 17 * Below you can see, in terms of #define's, the differences between the x86-64 14 18 * and the UML syscall table. ··· 22 18 #define sys_iopl sys_ni_syscall 23 19 #define sys_ioperm sys_ni_syscall 24 20 21 + #define __SYSCALL_NORETURN __SYSCALL 22 + 25 23 #define __SYSCALL(nr, sym) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); 26 24 #include <asm/syscalls_64.h> 25 + #undef __SYSCALL 27 26 28 - #undef __SYSCALL 29 27 #define __SYSCALL(nr, sym) sym, 30 - 31 - extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); 32 - 33 28 const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = { 34 29 #include <asm/syscalls_64.h> 35 30 };
+16 -2
scripts/syscalltbl.sh
··· 54 54 55 55 grep -E "^[0-9]+[[:space:]]+$abis" "$infile" | { 56 56 57 - while read nr abi name native compat ; do 57 + while read nr abi name native compat noreturn; do 58 58 59 59 if [ $nxt -gt $nr ]; then 60 60 echo "error: $infile: syscall table is not sorted or duplicates the same syscall number" >&2 ··· 66 66 nxt=$((nxt + 1)) 67 67 done 68 68 69 - if [ -n "$compat" ]; then 69 + if [ "$compat" = "-" ]; then 70 + unset compat 71 + fi 72 + 73 + if [ -n "$noreturn" ]; then 74 + if [ "$noreturn" != "noreturn" ]; then 75 + echo "error: $infile: invalid string \"$noreturn\" in 'noreturn' column" 76 + exit 1 77 + fi 78 + if [ -n "$compat" ]; then 79 + echo "__SYSCALL_COMPAT_NORETURN($nr, $native, $compat)" 80 + else 81 + echo "__SYSCALL_NORETURN($nr, $native)" 82 + fi 83 + elif [ -n "$compat" ]; then 70 84 echo "__SYSCALL_WITH_COMPAT($nr, $native, $compat)" 71 85 elif [ -n "$native" ]; then 72 86 echo "__SYSCALL($nr, $native)"
+4
tools/objtool/noreturns.h
··· 7 7 * Yes, this is unfortunate. A better solution is in the works. 8 8 */ 9 9 NORETURN(__fortify_panic) 10 + NORETURN(__ia32_sys_exit) 11 + NORETURN(__ia32_sys_exit_group) 10 12 NORETURN(__kunit_abort) 11 13 NORETURN(__module_put_and_kthread_exit) 12 14 NORETURN(__reiserfs_panic) 13 15 NORETURN(__stack_chk_fail) 14 16 NORETURN(__tdx_hypercall_failed) 15 17 NORETURN(__ubsan_handle_builtin_unreachable) 18 + NORETURN(__x64_sys_exit) 19 + NORETURN(__x64_sys_exit_group) 16 20 NORETURN(arch_cpu_idle_dead) 17 21 NORETURN(bch2_trans_in_restart_error) 18 22 NORETURN(bch2_trans_restart_error)