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