asmlinkage_protect replaces prevent_tail_call

The prevent_tail_call() macro works around the problem of the compiler
clobbering argument words on the stack, which for asmlinkage functions
is the caller's (user's) struct pt_regs. The tail/sibling-call
optimization is not the only way that the compiler can decide to use
stack argument words as scratch space, which we have to prevent.
Other optimizations can do it too.

Until we have new compiler support to make "asmlinkage" binding on the
compiler's own use of the stack argument frame, we have work around all
the manifestations of this issue that crop up.

More cases seem to be prevented by also keeping the incoming argument
variables live at the end of the function. This makes their original
stack slots attractive places to leave those variables, so the compiler
tends not clobber them for something else. It's still no guarantee, but
it handles some observed cases that prevent_tail_call() did not.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Roland McGrath and committed by Linus Torvalds 54a01510 783e391b

+44 -22
+2 -2
arch/x86/kernel/tls.c
··· 92 asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) 93 { 94 int ret = do_set_thread_area(current, -1, u_info, 1); 95 - prevent_tail_call(ret); 96 return ret; 97 } 98 ··· 142 asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) 143 { 144 int ret = do_get_thread_area(current, -1, u_info); 145 - prevent_tail_call(ret); 146 return ret; 147 } 148
··· 92 asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) 93 { 94 int ret = do_set_thread_area(current, -1, u_info, 1); 95 + asmlinkage_protect(1, ret, u_info); 96 return ret; 97 } 98 ··· 142 asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) 143 { 144 int ret = do_get_thread_area(current, -1, u_info); 145 + asmlinkage_protect(1, ret, u_info); 146 return ret; 147 } 148
+4 -4
fs/open.c
··· 335 { 336 long ret = do_sys_ftruncate(fd, length, 1); 337 /* avoid REGPARM breakage on x86: */ 338 - prevent_tail_call(ret); 339 return ret; 340 } 341 ··· 350 { 351 long ret = do_sys_ftruncate(fd, length, 0); 352 /* avoid REGPARM breakage on x86: */ 353 - prevent_tail_call(ret); 354 return ret; 355 } 356 #endif ··· 1067 1068 ret = do_sys_open(AT_FDCWD, filename, flags, mode); 1069 /* avoid REGPARM breakage on x86: */ 1070 - prevent_tail_call(ret); 1071 return ret; 1072 } 1073 ··· 1081 1082 ret = do_sys_open(dfd, filename, flags, mode); 1083 /* avoid REGPARM breakage on x86: */ 1084 - prevent_tail_call(ret); 1085 return ret; 1086 } 1087
··· 335 { 336 long ret = do_sys_ftruncate(fd, length, 1); 337 /* avoid REGPARM breakage on x86: */ 338 + asmlinkage_protect(2, ret, fd, length); 339 return ret; 340 } 341 ··· 350 { 351 long ret = do_sys_ftruncate(fd, length, 0); 352 /* avoid REGPARM breakage on x86: */ 353 + asmlinkage_protect(2, ret, fd, length); 354 return ret; 355 } 356 #endif ··· 1067 1068 ret = do_sys_open(AT_FDCWD, filename, flags, mode); 1069 /* avoid REGPARM breakage on x86: */ 1070 + asmlinkage_protect(3, ret, filename, flags, mode); 1071 return ret; 1072 } 1073 ··· 1081 1082 ret = do_sys_open(dfd, filename, flags, mode); 1083 /* avoid REGPARM breakage on x86: */ 1084 + asmlinkage_protect(4, ret, dfd, filename, flags, mode); 1085 return ret; 1086 } 1087
+23 -1
include/asm-x86/linkage.h
··· 8 9 #ifdef CONFIG_X86_32 10 #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) 11 - #define prevent_tail_call(ret) __asm__ ("" : "=r" (ret) : "0" (ret)) 12 /* 13 * For 32-bit UML - mark functions implemented in assembly that use 14 * regparm input parameters: 15 */ 16 #define asmregparm __attribute__((regparm(3))) 17 #endif 18 19 #ifdef CONFIG_X86_ALIGNMENT_16
··· 8 9 #ifdef CONFIG_X86_32 10 #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) 11 /* 12 * For 32-bit UML - mark functions implemented in assembly that use 13 * regparm input parameters: 14 */ 15 #define asmregparm __attribute__((regparm(3))) 16 + 17 + #define asmlinkage_protect(n, ret, args...) \ 18 + __asmlinkage_protect##n(ret, ##args) 19 + #define __asmlinkage_protect_n(ret, args...) \ 20 + __asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args) 21 + #define __asmlinkage_protect0(ret) \ 22 + __asmlinkage_protect_n(ret) 23 + #define __asmlinkage_protect1(ret, arg1) \ 24 + __asmlinkage_protect_n(ret, "g" (arg1)) 25 + #define __asmlinkage_protect2(ret, arg1, arg2) \ 26 + __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2)) 27 + #define __asmlinkage_protect3(ret, arg1, arg2, arg3) \ 28 + __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3)) 29 + #define __asmlinkage_protect4(ret, arg1, arg2, arg3, arg4) \ 30 + __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \ 31 + "g" (arg4)) 32 + #define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \ 33 + __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \ 34 + "g" (arg4), "g" (arg5)) 35 + #define __asmlinkage_protect6(ret, arg1, arg2, arg3, arg4, arg5, arg6) \ 36 + __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \ 37 + "g" (arg4), "g" (arg5), "g" (arg6)) 38 + 39 #endif 40 41 #ifdef CONFIG_X86_ALIGNMENT_16
+2 -2
include/linux/linkage.h
··· 17 # define asmregparm 18 #endif 19 20 - #ifndef prevent_tail_call 21 - # define prevent_tail_call(ret) do { } while (0) 22 #endif 23 24 #ifndef __ALIGN
··· 17 # define asmregparm 18 #endif 19 20 + #ifndef asmlinkage_protect 21 + # define asmlinkage_protect(n, ret, args...) do { } while (0) 22 #endif 23 24 #ifndef __ALIGN
+2 -2
kernel/exit.c
··· 1608 put_pid(pid); 1609 1610 /* avoid REGPARM breakage on x86: */ 1611 - prevent_tail_call(ret); 1612 return ret; 1613 } 1614 ··· 1640 put_pid(pid); 1641 1642 /* avoid REGPARM breakage on x86: */ 1643 - prevent_tail_call(ret); 1644 return ret; 1645 } 1646
··· 1608 put_pid(pid); 1609 1610 /* avoid REGPARM breakage on x86: */ 1611 + asmlinkage_protect(5, ret, which, upid, infop, options, ru); 1612 return ret; 1613 } 1614 ··· 1640 put_pid(pid); 1641 1642 /* avoid REGPARM breakage on x86: */ 1643 + asmlinkage_protect(4, ret, upid, stat_addr, options, ru); 1644 return ret; 1645 } 1646
+11 -11
kernel/uid16.c
··· 21 { 22 long ret = sys_chown(filename, low2highuid(user), low2highgid(group)); 23 /* avoid REGPARM breakage on x86: */ 24 - prevent_tail_call(ret); 25 return ret; 26 } 27 ··· 29 { 30 long ret = sys_lchown(filename, low2highuid(user), low2highgid(group)); 31 /* avoid REGPARM breakage on x86: */ 32 - prevent_tail_call(ret); 33 return ret; 34 } 35 ··· 37 { 38 long ret = sys_fchown(fd, low2highuid(user), low2highgid(group)); 39 /* avoid REGPARM breakage on x86: */ 40 - prevent_tail_call(ret); 41 return ret; 42 } 43 ··· 45 { 46 long ret = sys_setregid(low2highgid(rgid), low2highgid(egid)); 47 /* avoid REGPARM breakage on x86: */ 48 - prevent_tail_call(ret); 49 return ret; 50 } 51 ··· 53 { 54 long ret = sys_setgid(low2highgid(gid)); 55 /* avoid REGPARM breakage on x86: */ 56 - prevent_tail_call(ret); 57 return ret; 58 } 59 ··· 61 { 62 long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid)); 63 /* avoid REGPARM breakage on x86: */ 64 - prevent_tail_call(ret); 65 return ret; 66 } 67 ··· 69 { 70 long ret = sys_setuid(low2highuid(uid)); 71 /* avoid REGPARM breakage on x86: */ 72 - prevent_tail_call(ret); 73 return ret; 74 } 75 ··· 78 long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), 79 low2highuid(suid)); 80 /* avoid REGPARM breakage on x86: */ 81 - prevent_tail_call(ret); 82 return ret; 83 } 84 ··· 98 long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid), 99 low2highgid(sgid)); 100 /* avoid REGPARM breakage on x86: */ 101 - prevent_tail_call(ret); 102 return ret; 103 } 104 ··· 117 { 118 long ret = sys_setfsuid(low2highuid(uid)); 119 /* avoid REGPARM breakage on x86: */ 120 - prevent_tail_call(ret); 121 return ret; 122 } 123 ··· 125 { 126 long ret = sys_setfsgid(low2highgid(gid)); 127 /* avoid REGPARM breakage on x86: */ 128 - prevent_tail_call(ret); 129 return ret; 130 } 131
··· 21 { 22 long ret = sys_chown(filename, low2highuid(user), low2highgid(group)); 23 /* avoid REGPARM breakage on x86: */ 24 + asmlinkage_protect(3, ret, filename, user, group); 25 return ret; 26 } 27 ··· 29 { 30 long ret = sys_lchown(filename, low2highuid(user), low2highgid(group)); 31 /* avoid REGPARM breakage on x86: */ 32 + asmlinkage_protect(3, ret, filename, user, group); 33 return ret; 34 } 35 ··· 37 { 38 long ret = sys_fchown(fd, low2highuid(user), low2highgid(group)); 39 /* avoid REGPARM breakage on x86: */ 40 + asmlinkage_protect(3, ret, fd, user, group); 41 return ret; 42 } 43 ··· 45 { 46 long ret = sys_setregid(low2highgid(rgid), low2highgid(egid)); 47 /* avoid REGPARM breakage on x86: */ 48 + asmlinkage_protect(2, ret, rgid, egid); 49 return ret; 50 } 51 ··· 53 { 54 long ret = sys_setgid(low2highgid(gid)); 55 /* avoid REGPARM breakage on x86: */ 56 + asmlinkage_protect(1, ret, gid); 57 return ret; 58 } 59 ··· 61 { 62 long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid)); 63 /* avoid REGPARM breakage on x86: */ 64 + asmlinkage_protect(2, ret, ruid, euid); 65 return ret; 66 } 67 ··· 69 { 70 long ret = sys_setuid(low2highuid(uid)); 71 /* avoid REGPARM breakage on x86: */ 72 + asmlinkage_protect(1, ret, uid); 73 return ret; 74 } 75 ··· 78 long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), 79 low2highuid(suid)); 80 /* avoid REGPARM breakage on x86: */ 81 + asmlinkage_protect(3, ret, ruid, euid, suid); 82 return ret; 83 } 84 ··· 98 long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid), 99 low2highgid(sgid)); 100 /* avoid REGPARM breakage on x86: */ 101 + asmlinkage_protect(3, ret, rgid, egid, sgid); 102 return ret; 103 } 104 ··· 117 { 118 long ret = sys_setfsuid(low2highuid(uid)); 119 /* avoid REGPARM breakage on x86: */ 120 + asmlinkage_protect(1, ret, uid); 121 return ret; 122 } 123 ··· 125 { 126 long ret = sys_setfsgid(low2highgid(gid)); 127 /* avoid REGPARM breakage on x86: */ 128 + asmlinkage_protect(1, ret, gid); 129 return ret; 130 } 131