[patch 08/37] sparc: Fix fork/clone/vfork system call restart.

From: Greg KH
Date: Tue May 13 2008 - 16:15:41 EST


2.6.25-stable review patch. If anyone has any objections, please let us
know.

------------------
From: David S. Miller <davem@xxxxxxxxxxxxx>

[ Upstream commit: 1e38c126c9252b612697e34f43b1b3371c8ee31d ]

We clobber %i1 as well as %i0 for these system calls,
because they give two return values.

Therefore, on error, we have to restore %i1 properly
or else the restart explodes since it uses the wrong
arguments.

This fixes glibc's nptl/tst-eintr1.c testcase.

Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>

---
arch/sparc/kernel/process.c | 20 ++++++++++++++++----
arch/sparc64/kernel/process.c | 18 +++++++++++++++---
2 files changed, 31 insertions(+), 7 deletions(-)

--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -507,6 +507,8 @@ asmlinkage long sparc_do_fork(unsigned l
unsigned long stack_size)
{
int __user *parent_tid_ptr, *child_tid_ptr;
+ unsigned long orig_i1 = regs->u_regs[UREG_I1];
+ long ret;

#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) {
@@ -519,9 +521,19 @@ asmlinkage long sparc_do_fork(unsigned l
child_tid_ptr = (int __user *) regs->u_regs[UREG_I4];
}

- return do_fork(clone_flags, stack_start,
- regs, stack_size,
- parent_tid_ptr, child_tid_ptr);
+ ret = do_fork(clone_flags, stack_start,
+ regs, stack_size,
+ parent_tid_ptr, child_tid_ptr);
+
+ /* If we get an error and potentially restart the system
+ * call, we're screwed because copy_thread() clobbered
+ * the parent's %o1. So detect that case and restore it
+ * here.
+ */
+ if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
+ regs->u_regs[UREG_I1] = orig_i1;
+
+ return ret;
}

/* Copy a Sparc thread. The fork() return value conventions
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -421,14 +421,26 @@ asmlinkage int sparc_do_fork(unsigned lo
unsigned long stack_size)
{
unsigned long parent_tid_ptr, child_tid_ptr;
+ unsigned long orig_i1 = regs->u_regs[UREG_I1];
+ long ret;

parent_tid_ptr = regs->u_regs[UREG_I2];
child_tid_ptr = regs->u_regs[UREG_I4];

- return do_fork(clone_flags, stack_start,
- regs, stack_size,
- (int __user *) parent_tid_ptr,
- (int __user *) child_tid_ptr);
+ ret = do_fork(clone_flags, stack_start,
+ regs, stack_size,
+ (int __user *) parent_tid_ptr,
+ (int __user *) child_tid_ptr);
+
+ /* If we get an error and potentially restart the system
+ * call, we're screwed because copy_thread() clobbered
+ * the parent's %o1. So detect that case and restore it
+ * here.
+ */
+ if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
+ regs->u_regs[UREG_I1] = orig_i1;
+
+ return ret;
}

/* Copy a Sparc thread. The fork() return value conventions

--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/