aboutsummaryrefslogtreecommitdiff
path: root/arch/um/sys-x86_64
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2006-03-27 01:14:38 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-27 08:44:38 -0800
commit98c18238f146a9038098244d60a7d2b1f67ee790 (patch)
tree97149aafbc265e63cec55d37a655dabbbf64c232 /arch/um/sys-x86_64
parent6c29256c5703ad7c3cf62276dbc38d802a05669e (diff)
[PATCH] uml: fix segfault on signal delivery
This fixes a process segfault where a signal was being delivered such that a new stack page needed to be allocated to hold the signal frame. This was tripping some logic in the page fault handler which wouldn't allocate the page if the faulting address was more that 32 bytes lower than the current stack pointer. Since a signal frame is greater than 32 bytes, this exercised that case. It's fixed by updating the SP in the pt_regs before starting to copy the signal frame. Since those are the registers that will be copied on to the stack, we have to be careful to put the original SP, not the new one which points to the signal frame, on the stack. Signed-off-by: Jeff Dike <jdike@addtoit.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/um/sys-x86_64')
-rw-r--r--arch/um/sys-x86_64/signal.c56
1 files changed, 41 insertions, 15 deletions
diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c
index fe1d065332b..e75c4e1838b 100644
--- a/arch/um/sys-x86_64/signal.c
+++ b/arch/um/sys-x86_64/signal.c
@@ -55,7 +55,8 @@ static int copy_sc_from_user_skas(struct pt_regs *regs,
}
int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp,
- struct pt_regs *regs, unsigned long mask)
+ struct pt_regs *regs, unsigned long mask,
+ unsigned long sp)
{
struct faultinfo * fi = &current->thread.arch.faultinfo;
int err = 0;
@@ -70,7 +71,11 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp,
err |= PUTREG(regs, RDI, to, rdi);
err |= PUTREG(regs, RSI, to, rsi);
err |= PUTREG(regs, RBP, to, rbp);
- err |= PUTREG(regs, RSP, to, rsp);
+ /* Must use orignal RSP, which is passed in, rather than what's in
+ * the pt_regs, because that's already been updated to point at the
+ * signal frame.
+ */
+ err |= __put_user(sp, &to->rsp);
err |= PUTREG(regs, RBX, to, rbx);
err |= PUTREG(regs, RDX, to, rdx);
err |= PUTREG(regs, RCX, to, rcx);
@@ -102,7 +107,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp,
#ifdef CONFIG_MODE_TT
int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from,
- int fpsize)
+ int fpsize)
{
struct _fpstate *to_fp, *from_fp;
unsigned long sigs;
@@ -120,7 +125,7 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from,
}
int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp,
- struct sigcontext *from, int fpsize)
+ struct sigcontext *from, int fpsize, unsigned long sp)
{
struct _fpstate *to_fp, *from_fp;
int err;
@@ -128,11 +133,17 @@ int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp,
to_fp = (fp ? fp : (struct _fpstate *) (to + 1));
from_fp = from->fpstate;
err = copy_to_user(to, from, sizeof(*to));
+ /* The SP in the sigcontext is the updated one for the signal
+ * delivery. The sp passed in is the original, and this needs
+ * to be restored, so we stick it in separately.
+ */
+ err |= copy_to_user(&SC_SP(to), sp, sizeof(sp));
+
if(from_fp != NULL){
err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate));
err |= copy_to_user(to_fp, from_fp, fpsize);
}
- return(err);
+ return err;
}
#endif
@@ -148,11 +159,12 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from)
}
static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp,
- struct pt_regs *from, unsigned long mask)
+ struct pt_regs *from, unsigned long mask,
+ unsigned long sp)
{
return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs),
- sizeof(*fp)),
- copy_sc_to_user_skas(to, fp, from, mask)));
+ sizeof(*fp), sp),
+ copy_sc_to_user_skas(to, fp, from, mask, sp)));
}
struct rt_sigframe
@@ -170,6 +182,7 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
{
struct rt_sigframe __user *frame;
struct _fpstate __user *fp = NULL;
+ unsigned long save_sp = PT_REGS_RSP(regs);
int err = 0;
struct task_struct *me = current;
@@ -193,14 +206,25 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
goto out;
}
+ /* Update SP now because the page fault handler refuses to extend
+ * the stack if the faulting address is too far below the current
+ * SP, which frame now certainly is. If there's an error, the original
+ * value is restored on the way out.
+ * When writing the sigcontext to the stack, we have to write the
+ * original value, so that's passed to copy_sc_to_user, which does
+ * the right thing with it.
+ */
+ PT_REGS_RSP(regs) = (unsigned long) frame;
+
/* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
- err |= __put_user(sas_ss_flags(PT_REGS_SP(regs)),
+ err |= __put_user(sas_ss_flags(save_sp),
&frame->uc.uc_stack.ss_flags);
err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0]);
+ err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0],
+ save_sp);
err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate);
if (sizeof(*set) == 16) {
__put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
@@ -217,10 +241,10 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
else
/* could use a vstub here */
- goto out;
+ goto restore_sp;
if (err)
- goto out;
+ goto restore_sp;
/* Set up registers for signal handler */
{
@@ -238,10 +262,12 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
PT_REGS_RSI(regs) = (unsigned long) &frame->info;
PT_REGS_RDX(regs) = (unsigned long) &frame->uc;
PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler;
-
- PT_REGS_RSP(regs) = (unsigned long) frame;
out:
- return(err);
+ return err;
+
+restore_sp:
+ PT_REGS_RSP(regs) = save_sp;
+ return err;
}
long sys_rt_sigreturn(struct pt_regs *regs)