From c5cada670bf08d89870dd43ea25419f6ffb27c33 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 16 Oct 2004 19:20:30 -0700 Subject: [PATCH] [PATCH] tailcall prevention in sys_wait4() and sys_waitid() A hack to prevent the compiler from generatin tailcalls in these two functions. With CONFIG_REGPARM=y, the tailcalled code ends up stomping on the syscall's argument frame which corrupts userspace's registers. Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-i386/linkage.h | 4 ++++ include/linux/linkage.h | 4 ++++ kernel/exit.c | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/asm-i386/linkage.h b/include/asm-i386/linkage.h index e48009fd93c73..af3d8571c5c7c 100644 --- a/include/asm-i386/linkage.h +++ b/include/asm-i386/linkage.h @@ -5,6 +5,10 @@ #define FASTCALL(x) x __attribute__((regparm(3))) #define fastcall __attribute__((regparm(3))) +#ifdef CONFIG_REGPARM +# define prevent_tail_call(ret) __asm__ ("" : "=r" (ret) : "0" (ret)) +#endif + #ifdef CONFIG_X86_ALIGNMENT_16 #define __ALIGN .align 16,0x90 #define __ALIGN_STR ".align 16,0x90" diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 09955c0ce848a..338f7795d8a07 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -14,6 +14,10 @@ #define asmlinkage CPP_ASMLINKAGE #endif +#ifndef prevent_tail_call +# define prevent_tail_call(ret) do { } while (0) +#endif + #ifndef __ALIGN #define __ALIGN .align 4,0x90 #define __ALIGN_STR ".align 4,0x90" diff --git a/kernel/exit.c b/kernel/exit.c index 6860b509dd11f..6ec1f96fa92b6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1376,6 +1376,8 @@ asmlinkage long sys_waitid(int which, pid_t pid, struct siginfo __user *infop, int options, struct rusage __user *ru) { + long ret; + if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED)) return -EINVAL; if (!(options & (WEXITED|WSTOPPED|WCONTINUED))) @@ -1398,15 +1400,25 @@ asmlinkage long sys_waitid(int which, pid_t pid, return -EINVAL; } - return do_wait(pid, options, infop, NULL, ru); + ret = do_wait(pid, options, infop, NULL, ru); + + /* avoid REGPARM breakage on x86: */ + prevent_tail_call(ret); + return ret; } asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, int options, struct rusage __user *ru) { + long ret; + if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) return -EINVAL; - return do_wait(pid, options | WEXITED, NULL, stat_addr, ru); + ret = do_wait(pid, options | WEXITED, NULL, stat_addr, ru); + + /* avoid REGPARM breakage on x86: */ + prevent_tail_call(ret); + return ret; } #ifdef __ARCH_WANT_SYS_WAITPID -- 2.53.0