/*
* OK, we're invoking a handler
*/
-static int handle_signal(struct pt_regs *regs, unsigned long signr,
- struct k_sigaction *ka, siginfo_t *info,
- sigset_t *oldset)
+static int handle_signal(struct pt_regs *regs, unsigned long signr,
+ struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *oldset, int error)
{
+ __sighandler_t handler;
+ void (*restorer)(void);
unsigned long sp;
- int err;
+ sigset_t save;
+ int err, ret;
+ ret = 0;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
- /* Did we come from a system call? */
- if(PT_REGS_SYSCALL_NR(regs) >= 0){
- /* If so, check system call restarting.. */
- switch(PT_REGS_SYSCALL_RET(regs)){
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- PT_REGS_SYSCALL_RET(regs) = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART)) {
- PT_REGS_SYSCALL_RET(regs) = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- PT_REGS_RESTART_SYSCALL(regs);
- PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
+ switch(error){
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ ret = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ ret = -EINTR;
break;
}
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ PT_REGS_RESTART_SYSCALL(regs);
+ PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
+
+ /* This is because of the UM_SET_SYSCALL_RETURN and the fact
+ * that on i386 the system call number and return value are
+ * in the same register. When the system call restarts, %eax
+ * had better have the system call number in it. Since the
+ * return value doesn't matter (except that it shouldn't be
+ * -ERESTART*), we'll stick the system call number there.
+ */
+ ret = PT_REGS_SYSCALL_NR(regs);
+ break;
}
- sp = PT_REGS_SP(regs);
- if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
- sp = current->sas_ss_sp + current->sas_ss_size;
+ handler = ka->sa.sa_handler;
+ save = *oldset;
- if(ka->sa.sa_flags & SA_SIGINFO)
- err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset);
- else
- err = setup_signal_stack_sc(sp, signr, ka, regs, oldset);
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
- if(err){
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked = *oldset;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- force_sigsegv(signr, current);
- }
- else if(!(ka->sa.sa_flags & SA_NODEFER)){
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked, ¤t->blocked,
&ka->sa.sa_mask);
spin_unlock_irq(¤t->sighand->siglock);
}
- return err;
+ sp = PT_REGS_SP(regs);
+
+ if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ if(error != 0) PT_REGS_SET_SYSCALL_RETURN(regs, ret);
+
+ if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer;
+ else restorer = NULL;
+
+ if(ka->sa.sa_flags & SA_SIGINFO)
+ err = setup_signal_stack_si(sp, signr, (unsigned long) handler,
+ restorer, regs, info, &save);
+ else
+ err = setup_signal_stack_sc(sp, signr, (unsigned long) handler,
+ restorer, regs, &save);
+ if(err) goto segv;
+
+ return(0);
+ segv:
+ force_sigsegv(signr, current);
+ return(1);
}
-static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset)
+static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error)
{
struct k_sigaction ka_copy;
siginfo_t info;
- int sig, handled_sig = 0;
+ int err, sig;
- while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
- handled_sig = 1;
- /* Whee! Actually deliver the signal. */
- if(!handle_signal(regs, sig, &ka_copy, &info, oldset))
- break;
- }
+ if (!oldset)
+ oldset = ¤t->blocked;
+
+ sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL);
+ if(sig == 0)
+ return(0);
+
+ /* Whee! Actually deliver the signal. */
+ err = handle_signal(regs, sig, &ka_copy, &info, oldset, error);
+ if(!err)
+ return(1);
/* Did we come from a system call? */
- if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
+ if(PT_REGS_SYSCALL_NR(regs) >= 0){
/* Restart the system call - no handlers present */
if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
* on the host. The tracing thread will check this flag and
* PTRACE_SYSCALL if necessary.
*/
- if(current->ptrace & PT_DTRACE)
- current->thread.singlestep_syscall =
- is_syscall(PT_REGS_IP(¤t->thread.regs));
- return(handled_sig);
+ if((current->ptrace & PT_DTRACE) &&
+ is_syscall(PT_REGS_IP(¤t->thread.regs)))
+ (void) CHOOSE_MODE(current->thread.mode.tt.singlestep_syscall = 1, 0);
+ return(0);
}
-int do_signal(void)
+int do_signal(int error)
{
- return(kern_do_signal(¤t->thread.regs, ¤t->blocked));
+ return(kern_do_signal(¤t->thread.regs, NULL, error));
}
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
-long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
sigset_t saveset;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if(kern_do_signal(¤t->thread.regs, &saveset))
+ if(kern_do_signal(¤t->thread.regs, &saveset, -EINTR))
return(-EINTR);
}
}
-long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
{
sigset_t saveset, newset;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (kern_do_signal(¤t->thread.regs, &saveset))
+ if (kern_do_signal(¤t->thread.regs, &saveset, -EINTR))
return(-EINTR);
}
}
return ret;
}
-long sys_sigaltstack(const stack_t *uss, stack_t *uoss)
+int sys_sigaltstack(const stack_t *uss, stack_t *uoss)
{
return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs)));
}
return(ret);
}
-long sys_sigreturn(struct pt_regs regs)
+int sys_sigreturn(struct pt_regs regs)
{
void __user *sc = sp_to_sc(PT_REGS_SP(¤t->thread.regs));
void __user *mask = sp_to_mask(PT_REGS_SP(¤t->thread.regs));
return(PT_REGS_SYSCALL_RET(¤t->thread.regs));
}
-long sys_rt_sigreturn(struct pt_regs regs)
+int sys_rt_sigreturn(struct pt_regs regs)
{
unsigned long sp = PT_REGS_SP(¤t->thread.regs);
struct ucontext __user *uc = sp_to_uc(sp);