diff options
author | Roland McGrath <roland@redhat.com> | 2008-04-10 18:37:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-10 20:28:26 -0400 |
commit | 54a015104136974262afa4b8ddd943ea70dec8a2 (patch) | |
tree | 713f0c1f4d0afe62e5c568a424e309f70388cf7f | |
parent | 783e391b7b5b273cd20856d8f6f4878da8ec31b3 (diff) |
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>
-rw-r--r-- | arch/x86/kernel/tls.c | 4 | ||||
-rw-r--r-- | fs/open.c | 8 | ||||
-rw-r--r-- | include/asm-x86/linkage.h | 24 | ||||
-rw-r--r-- | include/linux/linkage.h | 4 | ||||
-rw-r--r-- | kernel/exit.c | 4 | ||||
-rw-r--r-- | kernel/uid16.c | 22 |
6 files changed, 44 insertions, 22 deletions
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c index 022bcaa3b42e..ab6bf375a307 100644 --- a/arch/x86/kernel/tls.c +++ b/arch/x86/kernel/tls.c | |||
@@ -92,7 +92,7 @@ int do_set_thread_area(struct task_struct *p, int idx, | |||
92 | asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) | 92 | asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) |
93 | { | 93 | { |
94 | int ret = do_set_thread_area(current, -1, u_info, 1); | 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 | return ret; | 96 | return ret; |
97 | } | 97 | } |
98 | 98 | ||
@@ -142,7 +142,7 @@ int do_get_thread_area(struct task_struct *p, int idx, | |||
142 | asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) | 142 | asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) |
143 | { | 143 | { |
144 | int ret = do_get_thread_area(current, -1, u_info); | 144 | int ret = do_get_thread_area(current, -1, u_info); |
145 | prevent_tail_call(ret); | 145 | asmlinkage_protect(1, ret, u_info); |
146 | return ret; | 146 | return ret; |
147 | } | 147 | } |
148 | 148 | ||
@@ -335,7 +335,7 @@ asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length) | |||
335 | { | 335 | { |
336 | long ret = do_sys_ftruncate(fd, length, 1); | 336 | long ret = do_sys_ftruncate(fd, length, 1); |
337 | /* avoid REGPARM breakage on x86: */ | 337 | /* avoid REGPARM breakage on x86: */ |
338 | prevent_tail_call(ret); | 338 | asmlinkage_protect(2, ret, fd, length); |
339 | return ret; | 339 | return ret; |
340 | } | 340 | } |
341 | 341 | ||
@@ -350,7 +350,7 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length) | |||
350 | { | 350 | { |
351 | long ret = do_sys_ftruncate(fd, length, 0); | 351 | long ret = do_sys_ftruncate(fd, length, 0); |
352 | /* avoid REGPARM breakage on x86: */ | 352 | /* avoid REGPARM breakage on x86: */ |
353 | prevent_tail_call(ret); | 353 | asmlinkage_protect(2, ret, fd, length); |
354 | return ret; | 354 | return ret; |
355 | } | 355 | } |
356 | #endif | 356 | #endif |
@@ -1067,7 +1067,7 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode) | |||
1067 | 1067 | ||
1068 | ret = do_sys_open(AT_FDCWD, filename, flags, mode); | 1068 | ret = do_sys_open(AT_FDCWD, filename, flags, mode); |
1069 | /* avoid REGPARM breakage on x86: */ | 1069 | /* avoid REGPARM breakage on x86: */ |
1070 | prevent_tail_call(ret); | 1070 | asmlinkage_protect(3, ret, filename, flags, mode); |
1071 | return ret; | 1071 | return ret; |
1072 | } | 1072 | } |
1073 | 1073 | ||
@@ -1081,7 +1081,7 @@ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, | |||
1081 | 1081 | ||
1082 | ret = do_sys_open(dfd, filename, flags, mode); | 1082 | ret = do_sys_open(dfd, filename, flags, mode); |
1083 | /* avoid REGPARM breakage on x86: */ | 1083 | /* avoid REGPARM breakage on x86: */ |
1084 | prevent_tail_call(ret); | 1084 | asmlinkage_protect(4, ret, dfd, filename, flags, mode); |
1085 | return ret; | 1085 | return ret; |
1086 | } | 1086 | } |
1087 | 1087 | ||
diff --git a/include/asm-x86/linkage.h b/include/asm-x86/linkage.h index 31739c7d66a9..d605eeba0f70 100644 --- a/include/asm-x86/linkage.h +++ b/include/asm-x86/linkage.h | |||
@@ -8,12 +8,34 @@ | |||
8 | 8 | ||
9 | #ifdef CONFIG_X86_32 | 9 | #ifdef CONFIG_X86_32 |
10 | #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) | 10 | #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) |
11 | #define prevent_tail_call(ret) __asm__ ("" : "=r" (ret) : "0" (ret)) | ||
12 | /* | 11 | /* |
13 | * For 32-bit UML - mark functions implemented in assembly that use | 12 | * For 32-bit UML - mark functions implemented in assembly that use |
14 | * regparm input parameters: | 13 | * regparm input parameters: |
15 | */ | 14 | */ |
16 | #define asmregparm __attribute__((regparm(3))) | 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 | #endif | 39 | #endif |
18 | 40 | ||
19 | #ifdef CONFIG_X86_ALIGNMENT_16 | 41 | #ifdef CONFIG_X86_ALIGNMENT_16 |
diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 0592936344c4..fe2a39c489b6 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h | |||
@@ -17,8 +17,8 @@ | |||
17 | # define asmregparm | 17 | # define asmregparm |
18 | #endif | 18 | #endif |
19 | 19 | ||
20 | #ifndef prevent_tail_call | 20 | #ifndef asmlinkage_protect |
21 | # define prevent_tail_call(ret) do { } while (0) | 21 | # define asmlinkage_protect(n, ret, args...) do { } while (0) |
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | #ifndef __ALIGN | 24 | #ifndef __ALIGN |
diff --git a/kernel/exit.c b/kernel/exit.c index 53872bf993fa..073005b1cfb2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -1608,7 +1608,7 @@ asmlinkage long sys_waitid(int which, pid_t upid, | |||
1608 | put_pid(pid); | 1608 | put_pid(pid); |
1609 | 1609 | ||
1610 | /* avoid REGPARM breakage on x86: */ | 1610 | /* avoid REGPARM breakage on x86: */ |
1611 | prevent_tail_call(ret); | 1611 | asmlinkage_protect(5, ret, which, upid, infop, options, ru); |
1612 | return ret; | 1612 | return ret; |
1613 | } | 1613 | } |
1614 | 1614 | ||
@@ -1640,7 +1640,7 @@ asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr, | |||
1640 | put_pid(pid); | 1640 | put_pid(pid); |
1641 | 1641 | ||
1642 | /* avoid REGPARM breakage on x86: */ | 1642 | /* avoid REGPARM breakage on x86: */ |
1643 | prevent_tail_call(ret); | 1643 | asmlinkage_protect(4, ret, upid, stat_addr, options, ru); |
1644 | return ret; | 1644 | return ret; |
1645 | } | 1645 | } |
1646 | 1646 | ||
diff --git a/kernel/uid16.c b/kernel/uid16.c index dd308ba4e03b..3e41c1673e2f 100644 --- a/kernel/uid16.c +++ b/kernel/uid16.c | |||
@@ -21,7 +21,7 @@ asmlinkage long sys_chown16(const char __user * filename, old_uid_t user, old_gi | |||
21 | { | 21 | { |
22 | long ret = sys_chown(filename, low2highuid(user), low2highgid(group)); | 22 | long ret = sys_chown(filename, low2highuid(user), low2highgid(group)); |
23 | /* avoid REGPARM breakage on x86: */ | 23 | /* avoid REGPARM breakage on x86: */ |
24 | prevent_tail_call(ret); | 24 | asmlinkage_protect(3, ret, filename, user, group); |
25 | return ret; | 25 | return ret; |
26 | } | 26 | } |
27 | 27 | ||
@@ -29,7 +29,7 @@ asmlinkage long sys_lchown16(const char __user * filename, old_uid_t user, old_g | |||
29 | { | 29 | { |
30 | long ret = sys_lchown(filename, low2highuid(user), low2highgid(group)); | 30 | long ret = sys_lchown(filename, low2highuid(user), low2highgid(group)); |
31 | /* avoid REGPARM breakage on x86: */ | 31 | /* avoid REGPARM breakage on x86: */ |
32 | prevent_tail_call(ret); | 32 | asmlinkage_protect(3, ret, filename, user, group); |
33 | return ret; | 33 | return ret; |
34 | } | 34 | } |
35 | 35 | ||
@@ -37,7 +37,7 @@ asmlinkage long sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group) | |||
37 | { | 37 | { |
38 | long ret = sys_fchown(fd, low2highuid(user), low2highgid(group)); | 38 | long ret = sys_fchown(fd, low2highuid(user), low2highgid(group)); |
39 | /* avoid REGPARM breakage on x86: */ | 39 | /* avoid REGPARM breakage on x86: */ |
40 | prevent_tail_call(ret); | 40 | asmlinkage_protect(3, ret, fd, user, group); |
41 | return ret; | 41 | return ret; |
42 | } | 42 | } |
43 | 43 | ||
@@ -45,7 +45,7 @@ asmlinkage long sys_setregid16(old_gid_t rgid, old_gid_t egid) | |||
45 | { | 45 | { |
46 | long ret = sys_setregid(low2highgid(rgid), low2highgid(egid)); | 46 | long ret = sys_setregid(low2highgid(rgid), low2highgid(egid)); |
47 | /* avoid REGPARM breakage on x86: */ | 47 | /* avoid REGPARM breakage on x86: */ |
48 | prevent_tail_call(ret); | 48 | asmlinkage_protect(2, ret, rgid, egid); |
49 | return ret; | 49 | return ret; |
50 | } | 50 | } |
51 | 51 | ||
@@ -53,7 +53,7 @@ asmlinkage long sys_setgid16(old_gid_t gid) | |||
53 | { | 53 | { |
54 | long ret = sys_setgid(low2highgid(gid)); | 54 | long ret = sys_setgid(low2highgid(gid)); |
55 | /* avoid REGPARM breakage on x86: */ | 55 | /* avoid REGPARM breakage on x86: */ |
56 | prevent_tail_call(ret); | 56 | asmlinkage_protect(1, ret, gid); |
57 | return ret; | 57 | return ret; |
58 | } | 58 | } |
59 | 59 | ||
@@ -61,7 +61,7 @@ asmlinkage long sys_setreuid16(old_uid_t ruid, old_uid_t euid) | |||
61 | { | 61 | { |
62 | long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid)); | 62 | long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid)); |
63 | /* avoid REGPARM breakage on x86: */ | 63 | /* avoid REGPARM breakage on x86: */ |
64 | prevent_tail_call(ret); | 64 | asmlinkage_protect(2, ret, ruid, euid); |
65 | return ret; | 65 | return ret; |
66 | } | 66 | } |
67 | 67 | ||
@@ -69,7 +69,7 @@ asmlinkage long sys_setuid16(old_uid_t uid) | |||
69 | { | 69 | { |
70 | long ret = sys_setuid(low2highuid(uid)); | 70 | long ret = sys_setuid(low2highuid(uid)); |
71 | /* avoid REGPARM breakage on x86: */ | 71 | /* avoid REGPARM breakage on x86: */ |
72 | prevent_tail_call(ret); | 72 | asmlinkage_protect(1, ret, uid); |
73 | return ret; | 73 | return ret; |
74 | } | 74 | } |
75 | 75 | ||
@@ -78,7 +78,7 @@ asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid) | |||
78 | long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), | 78 | long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), |
79 | low2highuid(suid)); | 79 | low2highuid(suid)); |
80 | /* avoid REGPARM breakage on x86: */ | 80 | /* avoid REGPARM breakage on x86: */ |
81 | prevent_tail_call(ret); | 81 | asmlinkage_protect(3, ret, ruid, euid, suid); |
82 | return ret; | 82 | return ret; |
83 | } | 83 | } |
84 | 84 | ||
@@ -98,7 +98,7 @@ asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid) | |||
98 | long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid), | 98 | long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid), |
99 | low2highgid(sgid)); | 99 | low2highgid(sgid)); |
100 | /* avoid REGPARM breakage on x86: */ | 100 | /* avoid REGPARM breakage on x86: */ |
101 | prevent_tail_call(ret); | 101 | asmlinkage_protect(3, ret, rgid, egid, sgid); |
102 | return ret; | 102 | return ret; |
103 | } | 103 | } |
104 | 104 | ||
@@ -117,7 +117,7 @@ asmlinkage long sys_setfsuid16(old_uid_t uid) | |||
117 | { | 117 | { |
118 | long ret = sys_setfsuid(low2highuid(uid)); | 118 | long ret = sys_setfsuid(low2highuid(uid)); |
119 | /* avoid REGPARM breakage on x86: */ | 119 | /* avoid REGPARM breakage on x86: */ |
120 | prevent_tail_call(ret); | 120 | asmlinkage_protect(1, ret, uid); |
121 | return ret; | 121 | return ret; |
122 | } | 122 | } |
123 | 123 | ||
@@ -125,7 +125,7 @@ asmlinkage long sys_setfsgid16(old_gid_t gid) | |||
125 | { | 125 | { |
126 | long ret = sys_setfsgid(low2highgid(gid)); | 126 | long ret = sys_setfsgid(low2highgid(gid)); |
127 | /* avoid REGPARM breakage on x86: */ | 127 | /* avoid REGPARM breakage on x86: */ |
128 | prevent_tail_call(ret); | 128 | asmlinkage_protect(1, ret, gid); |
129 | return ret; | 129 | return ret; |
130 | } | 130 | } |
131 | 131 | ||