Skip to content

Commit

Permalink
asmlinkage_protect replaces prevent_tail_call
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Roland McGrath authored and torvalds committed Apr 11, 2008
1 parent 783e391 commit 54a0151
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 22 deletions.
4 changes: 2 additions & 2 deletions arch/x86/kernel/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ int do_set_thread_area(struct task_struct *p, int idx,
asmlinkage int sys_set_thread_area(struct user_desc __user *u_info)
{
int ret = do_set_thread_area(current, -1, u_info, 1);
prevent_tail_call(ret);
asmlinkage_protect(1, ret, u_info);
return ret;
}

Expand Down Expand Up @@ -142,7 +142,7 @@ int do_get_thread_area(struct task_struct *p, int idx,
asmlinkage int sys_get_thread_area(struct user_desc __user *u_info)
{
int ret = do_get_thread_area(current, -1, u_info);
prevent_tail_call(ret);
asmlinkage_protect(1, ret, u_info);
return ret;
}

Expand Down
8 changes: 4 additions & 4 deletions fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length)
{
long ret = do_sys_ftruncate(fd, length, 1);
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(2, ret, fd, length);
return ret;
}

Expand All @@ -350,7 +350,7 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
{
long ret = do_sys_ftruncate(fd, length, 0);
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(2, ret, fd, length);
return ret;
}
#endif
Expand Down Expand Up @@ -1067,7 +1067,7 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode)

ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}

Expand All @@ -1081,7 +1081,7 @@ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,

ret = do_sys_open(dfd, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(4, ret, dfd, filename, flags, mode);
return ret;
}

Expand Down
24 changes: 23 additions & 1 deletion include/asm-x86/linkage.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,34 @@

#ifdef CONFIG_X86_32
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
#define prevent_tail_call(ret) __asm__ ("" : "=r" (ret) : "0" (ret))
/*
* For 32-bit UML - mark functions implemented in assembly that use
* regparm input parameters:
*/
#define asmregparm __attribute__((regparm(3)))

#define asmlinkage_protect(n, ret, args...) \
__asmlinkage_protect##n(ret, ##args)
#define __asmlinkage_protect_n(ret, args...) \
__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args)
#define __asmlinkage_protect0(ret) \
__asmlinkage_protect_n(ret)
#define __asmlinkage_protect1(ret, arg1) \
__asmlinkage_protect_n(ret, "g" (arg1))
#define __asmlinkage_protect2(ret, arg1, arg2) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2))
#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3))
#define __asmlinkage_protect4(ret, arg1, arg2, arg3, arg4) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
"g" (arg4))
#define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
"g" (arg4), "g" (arg5))
#define __asmlinkage_protect6(ret, arg1, arg2, arg3, arg4, arg5, arg6) \
__asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
"g" (arg4), "g" (arg5), "g" (arg6))

#endif

#ifdef CONFIG_X86_ALIGNMENT_16
Expand Down
4 changes: 2 additions & 2 deletions include/linux/linkage.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
# define asmregparm
#endif

#ifndef prevent_tail_call
# define prevent_tail_call(ret) do { } while (0)
#ifndef asmlinkage_protect
# define asmlinkage_protect(n, ret, args...) do { } while (0)
#endif

#ifndef __ALIGN
Expand Down
4 changes: 2 additions & 2 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1608,7 +1608,7 @@ asmlinkage long sys_waitid(int which, pid_t upid,
put_pid(pid);

/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(5, ret, which, upid, infop, options, ru);
return ret;
}

Expand Down Expand Up @@ -1640,7 +1640,7 @@ asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
put_pid(pid);

/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(4, ret, upid, stat_addr, options, ru);
return ret;
}

Expand Down
22 changes: 11 additions & 11 deletions kernel/uid16.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,55 @@ asmlinkage long sys_chown16(const char __user * filename, old_uid_t user, old_gi
{
long ret = sys_chown(filename, low2highuid(user), low2highgid(group));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, filename, user, group);
return ret;
}

asmlinkage long sys_lchown16(const char __user * filename, old_uid_t user, old_gid_t group)
{
long ret = sys_lchown(filename, low2highuid(user), low2highgid(group));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, filename, user, group);
return ret;
}

asmlinkage long sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group)
{
long ret = sys_fchown(fd, low2highuid(user), low2highgid(group));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, fd, user, group);
return ret;
}

asmlinkage long sys_setregid16(old_gid_t rgid, old_gid_t egid)
{
long ret = sys_setregid(low2highgid(rgid), low2highgid(egid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(2, ret, rgid, egid);
return ret;
}

asmlinkage long sys_setgid16(old_gid_t gid)
{
long ret = sys_setgid(low2highgid(gid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(1, ret, gid);
return ret;
}

asmlinkage long sys_setreuid16(old_uid_t ruid, old_uid_t euid)
{
long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(2, ret, ruid, euid);
return ret;
}

asmlinkage long sys_setuid16(old_uid_t uid)
{
long ret = sys_setuid(low2highuid(uid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(1, ret, uid);
return ret;
}

Expand All @@ -78,7 +78,7 @@ asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)
long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid),
low2highuid(suid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, ruid, euid, suid);
return ret;
}

Expand All @@ -98,7 +98,7 @@ asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)
long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid),
low2highgid(sgid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(3, ret, rgid, egid, sgid);
return ret;
}

Expand All @@ -117,15 +117,15 @@ asmlinkage long sys_setfsuid16(old_uid_t uid)
{
long ret = sys_setfsuid(low2highuid(uid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(1, ret, uid);
return ret;
}

asmlinkage long sys_setfsgid16(old_gid_t gid)
{
long ret = sys_setfsgid(low2highgid(gid));
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
asmlinkage_protect(1, ret, gid);
return ret;
}

Expand Down

0 comments on commit 54a0151

Please sign in to comment.