diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 364 | ||||
| -rw-r--r-- | kernel/fork.c | 91 | ||||
| -rw-r--r-- | kernel/kmod.c | 12 | ||||
| -rw-r--r-- | kernel/ptrace.c | 36 | ||||
| -rw-r--r-- | kernel/signal.c | 1274 | ||||
| -rw-r--r-- | kernel/suspend.c | 7 | ||||
| -rw-r--r-- | kernel/workqueue.c | 9 |
7 files changed, 1132 insertions, 661 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 057c562f62b1..fbc00cfae030 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -76,6 +76,7 @@ void release_task(struct task_struct * p) if (unlikely(p->ptrace)) __ptrace_unlink(p); BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); + __exit_signal(p); __exit_sighand(p); proc_dentry = __unhash_process(p); @@ -198,6 +199,17 @@ static inline int has_stopped_jobs(int pgrp) for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { if (p->state != TASK_STOPPED) continue; + + /* If p is stopped by a debugger on a signal that won't + stop it, then don't count p as stopped. This isn't + perfect but it's a good approximation. */ + if (unlikely (p->ptrace) + && p->exit_code != SIGSTOP + && p->exit_code != SIGTSTP + && p->exit_code != SIGTTOU + && p->exit_code != SIGTTIN) + continue; + retval = 1; break; } @@ -542,10 +554,33 @@ static inline void forget_original_parent(struct task_struct * father) * Send signals to all our closest relatives so that they know * to properly mourn us.. */ -static void exit_notify(void) +static void exit_notify(struct task_struct *tsk) { struct task_struct *t; + if (signal_pending(tsk) && !tsk->signal->group_exit + && !thread_group_empty(tsk)) { + /* + * This occurs when there was a race between our exit + * syscall and a group signal choosing us as the one to + * wake up. It could be that we are the only thread + * alerted to check for pending signals, but another thread + * should be woken now to take the signal since we will not. + * Now we'll wake all the threads in the group just to make + * sure someone gets all the pending signals. + */ + read_lock(&tasklist_lock); + spin_lock_irq(&tsk->sighand->siglock); + for (t = next_thread(tsk); t != tsk; t = next_thread(t)) + if (!signal_pending(t) && !(t->flags & PF_EXITING)) { + recalc_sigpending_tsk(t); + if (signal_pending(t)) + signal_wake_up(t, 0); + } + spin_unlock_irq(&tsk->sighand->siglock); + read_unlock(&tasklist_lock); + } + write_lock_irq(&tasklist_lock); /* @@ -557,8 +592,8 @@ static void exit_notify(void) * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) */ - forget_original_parent(current); - BUG_ON(!list_empty(¤t->children)); + forget_original_parent(tsk); + BUG_ON(!list_empty(&tsk->children)); /* * Check to see if any process groups have become orphaned @@ -570,14 +605,14 @@ static void exit_notify(void) * is about to become orphaned. */ - t = current->parent; + t = tsk->real_parent; - if ((t->pgrp != current->pgrp) && - (t->session == current->session) && - will_become_orphaned_pgrp(current->pgrp, current) && - has_stopped_jobs(current->pgrp)) { - __kill_pg_info(SIGHUP, (void *)1, current->pgrp); - __kill_pg_info(SIGCONT, (void *)1, current->pgrp); + if ((t->pgrp != tsk->pgrp) && + (t->session == tsk->session) && + will_become_orphaned_pgrp(tsk->pgrp, tsk) && + has_stopped_jobs(tsk->pgrp)) { + __kill_pg_info(SIGHUP, (void *)1, tsk->pgrp); + __kill_pg_info(SIGCONT, (void *)1, tsk->pgrp); } /* Let father know we died @@ -596,17 +631,25 @@ static void exit_notify(void) * */ - if (current->exit_signal != SIGCHLD && current->exit_signal != -1 && - ( current->parent_exec_id != t->self_exec_id || - current->self_exec_id != current->parent_exec_id) + if (tsk->exit_signal != SIGCHLD && tsk->exit_signal != -1 && + ( tsk->parent_exec_id != t->self_exec_id || + tsk->self_exec_id != tsk->parent_exec_id) && !capable(CAP_KILL)) - current->exit_signal = SIGCHLD; + tsk->exit_signal = SIGCHLD; - if (current->exit_signal != -1) - do_notify_parent(current, current->exit_signal); + /* If something other than our normal parent is ptracing us, then + * send it a SIGCHLD instead of honoring exit_signal. exit_signal + * only has special meaning to our real parent. + */ + if (tsk->exit_signal != -1) { + if (tsk->parent == tsk->real_parent) + do_notify_parent(tsk, tsk->exit_signal); + else + do_notify_parent(tsk, SIGCHLD); + } - current->state = TASK_ZOMBIE; + tsk->state = TASK_ZOMBIE; /* * No need to unlock IRQs, we'll schedule() immediately * anyway. In the preemption case this also makes it @@ -637,7 +680,9 @@ NORET_TYPE void do_exit(long code) profile_exit_task(tsk); -fake_volatile: + if (unlikely(current->ptrace & PT_TRACE_EXIT)) + ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); + acct_process(code); __exit_mm(tsk); @@ -647,7 +692,7 @@ fake_volatile: exit_namespace(tsk); exit_thread(); - if (current->leader) + if (tsk->leader) disassociate_ctty(1); module_put(tsk->thread_info->exec_domain->module); @@ -655,26 +700,16 @@ fake_volatile: module_put(tsk->binfmt->module); tsk->exit_code = code; - exit_notify(); + exit_notify(tsk); preempt_disable(); - if (current->exit_signal == -1) - release_task(current); + + if (tsk->exit_signal == -1) + release_task(tsk); + schedule(); BUG(); -/* - * In order to get rid of the "volatile function does return" message - * I did this little loop that confuses gcc to think do_exit really - * is volatile. In fact it's schedule() that is volatile in some - * circumstances: when current->state = ZOMBIE, schedule() never - * returns. - * - * In fact the natural way to do all this is to have the label and the - * goto right after each other, but I put the fake_volatile label at - * the start of the function just in case something /really/ bad - * happens, and the schedule returns. This way we can try again. I'm - * not paranoid: it's just that everybody is out to get me. - */ - goto fake_volatile; + /* Avoid "noreturn function does return". */ + for (;;) ; } NORET_TYPE void complete_and_exit(struct completion *comp, long code) @@ -696,9 +731,9 @@ task_t *next_thread(task_t *p) struct list_head *tmp, *head = &link->pidptr->task_list; #if CONFIG_SMP - if (!p->sig) + if (!p->sighand) BUG(); - if (!spin_is_locked(&p->sig->siglock) && + if (!spin_is_locked(&p->sighand->siglock) && !rwlock_is_locked(&tasklist_lock)) BUG(); #endif @@ -710,31 +745,45 @@ task_t *next_thread(task_t *p) } /* - * this kills every thread in the thread group. Note that any externally - * wait4()-ing process will get the correct exit code - even if this - * thread is not the thread group leader. + * Take down every thread in the group. This is called by fatal signals + * as well as by sys_exit_group (below). */ -asmlinkage long sys_exit_group(int error_code) +NORET_TYPE void +do_group_exit(int exit_code) { - unsigned int exit_code = (error_code & 0xff) << 8; - - if (!thread_group_empty(current)) { - struct signal_struct *sig = current->sig; - - spin_lock_irq(&sig->siglock); - if (sig->group_exit) { - spin_unlock_irq(&sig->siglock); - - /* another thread was faster: */ - do_exit(sig->group_exit_code); + BUG_ON(exit_code & 0x80); /* core dumps don't get here */ + + if (current->signal->group_exit) + exit_code = current->signal->group_exit_code; + else if (!thread_group_empty(current)) { + struct signal_struct *const sig = current->signal; + struct sighand_struct *const sighand = current->sighand; + read_lock(&tasklist_lock); + spin_lock_irq(&sighand->siglock); + if (sig->group_exit) + /* Another thread got here before we took the lock. */ + exit_code = sig->group_exit_code; + else { + sig->group_exit = 1; + sig->group_exit_code = exit_code; + zap_other_threads(current); } - sig->group_exit = 1; - sig->group_exit_code = exit_code; - __broadcast_thread_group(current, SIGKILL); - spin_unlock_irq(&sig->siglock); + spin_unlock_irq(&sighand->siglock); + read_unlock(&tasklist_lock); } do_exit(exit_code); + /* NOTREACHED */ +} + +/* + * this kills every thread in the thread group. Note that any externally + * wait4()-ing process will get the correct exit code - even if this + * thread is not the thread group leader. + */ +asmlinkage long sys_exit_group(int error_code) +{ + do_group_exit((error_code & 0xff) << 8); } static int eligible_child(pid_t pid, int options, task_t *p) @@ -778,11 +827,149 @@ static int eligible_child(pid_t pid, int options, task_t *p) return 1; } +/* + * Handle sys_wait4 work for one task in state TASK_ZOMBIE. We hold + * read_lock(&tasklist_lock) on entry. If we return zero, we still hold + * the lock and this task is uninteresting. If we return nonzero, we have + * released the lock and the system call should return. + */ +static int wait_task_zombie(task_t *p, unsigned int *stat_addr, struct rusage *ru) +{ + unsigned long state; + int retval; + + /* + * Try to move the task's state to DEAD + * only one thread is allowed to do this: + */ + state = xchg(&p->state, TASK_DEAD); + if (state != TASK_ZOMBIE) { + BUG_ON(state != TASK_DEAD); + return 0; + } + if (unlikely(p->exit_signal == -1)) + /* + * This can only happen in a race with a ptraced thread + * dying on another processor. + */ + return 0; + + /* + * Now we are sure this task is interesting, and no other + * thread can reap it because we set its state to TASK_DEAD. + */ + read_unlock(&tasklist_lock); + + retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; + if (!retval && stat_addr) { + if (p->signal->group_exit) + retval = put_user(p->signal->group_exit_code, stat_addr); + else + retval = put_user(p->exit_code, stat_addr); + } + if (retval) { + p->state = TASK_ZOMBIE; + return retval; + } + retval = p->pid; + if (p->real_parent != p->parent) { + write_lock_irq(&tasklist_lock); + /* Double-check with lock held. */ + if (p->real_parent != p->parent) { + __ptrace_unlink(p); + do_notify_parent(p, p->exit_signal); + p->state = TASK_ZOMBIE; + p = NULL; + } + write_unlock_irq(&tasklist_lock); + } + if (p != NULL) + release_task(p); + BUG_ON(!retval); + return retval; +} + +/* + * Handle sys_wait4 work for one task in state TASK_STOPPED. We hold + * read_lock(&tasklist_lock) on entry. If we return zero, we still hold + * the lock and this task is uninteresting. If we return nonzero, we have + * released the lock and the system call should return. + */ +static int wait_task_stopped(task_t *p, int delayed_group_leader, + unsigned int *stat_addr, struct rusage *ru) +{ + int retval, exit_code; + + if (!p->exit_code) + return 0; + if (delayed_group_leader && !(p->ptrace & PT_PTRACED) && + p->signal && p->signal->group_stop_count > 0) + /* + * A group stop is in progress and this is the group leader. + * We won't report until all threads have stopped. + */ + return 0; + + /* + * Now we are pretty sure this task is interesting. + * Make sure it doesn't get reaped out from under us while we + * give up the lock and then examine it below. We don't want to + * keep holding onto the tasklist_lock while we call getrusage and + * possibly take page faults for user memory. + */ + get_task_struct(p); + read_unlock(&tasklist_lock); + write_lock_irq(&tasklist_lock); + + /* + * This uses xchg to be atomic with the thread resuming and setting + * it. It must also be done with the write lock held to prevent a + * race with the TASK_ZOMBIE case. + */ + exit_code = xchg(&p->exit_code, 0); + if (unlikely(p->state > TASK_STOPPED)) { + /* + * The task resumed and then died. Let the next iteration + * catch it in TASK_ZOMBIE. Note that exit_code might + * already be zero here if it resumed and did _exit(0). + * The task itself is dead and won't touch exit_code again; + * other processors in this function are locked out. + */ + p->exit_code = exit_code; + exit_code = 0; + } + if (unlikely(exit_code == 0)) { + /* + * Another thread in this function got to it first, or it + * resumed, or it resumed and then died. + */ + write_unlock_irq(&tasklist_lock); + put_task_struct(p); + read_lock(&tasklist_lock); + return 0; + } + + /* move to end of parent's list to avoid starvation */ + remove_parent(p); + add_parent(p, p->parent); + + write_unlock_irq(&tasklist_lock); + + retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; + if (!retval && stat_addr) + retval = put_user((exit_code << 8) | 0x7f, stat_addr); + if (!retval) + retval = p->pid; + put_task_struct(p); + + BUG_ON(!retval); + return retval; +} + asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru) { DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; - unsigned long state; int flag, retval; if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) @@ -809,63 +996,24 @@ repeat: switch (p->state) { case TASK_STOPPED: - if (!p->exit_code) - continue; - if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED)) + if (!(options & WUNTRACED) && + !(p->ptrace & PT_PTRACED)) continue; - read_unlock(&tasklist_lock); - - /* move to end of parent's list to avoid starvation */ - write_lock_irq(&tasklist_lock); - remove_parent(p); - add_parent(p, p->parent); - write_unlock_irq(&tasklist_lock); - retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; - if (!retval && stat_addr) - retval = put_user((p->exit_code << 8) | 0x7f, stat_addr); - if (!retval) { - p->exit_code = 0; - retval = p->pid; - } - goto end_wait4; + retval = wait_task_stopped(p, ret == 2, + stat_addr, ru); + if (retval != 0) /* He released the lock. */ + goto end_wait4; + break; case TASK_ZOMBIE: /* * Eligible but we cannot release it yet: */ if (ret == 2) continue; - /* - * Try to move the task's state to DEAD - * only one thread is allowed to do this: - */ - state = xchg(&p->state, TASK_DEAD); - if (state != TASK_ZOMBIE) - continue; - read_unlock(&tasklist_lock); - - retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; - if (!retval && stat_addr) { - if (p->sig->group_exit) - retval = put_user(p->sig->group_exit_code, stat_addr); - else - retval = put_user(p->exit_code, stat_addr); - } - if (retval) { - p->state = TASK_ZOMBIE; + retval = wait_task_zombie(p, stat_addr, ru); + if (retval != 0) /* He released the lock. */ goto end_wait4; - } - retval = p->pid; - if (p->real_parent != p->parent) { - write_lock_irq(&tasklist_lock); - __ptrace_unlink(p); - do_notify_parent(p, SIGCHLD); - p->state = TASK_ZOMBIE; - write_unlock_irq(&tasklist_lock); - } else - release_task(p); - goto end_wait4; - default: - continue; + break; } } if (!flag) { @@ -880,7 +1028,7 @@ repeat: if (options & __WNOTHREAD) break; tsk = next_thread(tsk); - if (tsk->sig != current->sig) + if (tsk->signal != current->signal) BUG(); } while (tsk != current); read_unlock(&tasklist_lock); diff --git a/kernel/fork.c b/kernel/fork.c index 4fc3fcd5dacb..9e12b35e3924 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -665,22 +665,39 @@ out_release: static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { - struct signal_struct *sig; + struct sighand_struct *sig; - if (clone_flags & CLONE_SIGHAND) { - atomic_inc(¤t->sig->count); + if (clone_flags & (CLONE_SIGHAND | CLONE_THREAD)) { + atomic_inc(¤t->sighand->count); return 0; } - sig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL); - tsk->sig = sig; + sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL); + tsk->sighand = sig; if (!sig) return -1; spin_lock_init(&sig->siglock); atomic_set(&sig->count, 1); + memcpy(sig->action, current->sighand->action, sizeof(sig->action)); + return 0; +} + +static inline int copy_signal(unsigned long clone_flags, struct task_struct * tsk) +{ + struct signal_struct *sig; + + if (clone_flags & CLONE_THREAD) { + atomic_inc(¤t->signal->count); + return 0; + } + sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL); + tsk->signal = sig; + if (!sig) + return -1; + atomic_set(&sig->count, 1); sig->group_exit = 0; sig->group_exit_code = 0; sig->group_exit_task = NULL; - memcpy(sig->action, current->sig->action, sizeof(sig->action)); + sig->group_stop_count = 0; sig->curr_target = NULL; init_sigpending(&sig->shared_pending); @@ -801,7 +818,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, spin_lock_init(&p->alloc_lock); spin_lock_init(&p->switch_lock); - clear_tsk_thread_flag(p,TIF_SIGPENDING); + clear_tsk_thread_flag(p, TIF_SIGPENDING); init_sigpending(&p->pending); p->it_real_value = p->it_virt_value = p->it_prof_value = 0; @@ -830,8 +847,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_cleanup_files; if (copy_sighand(clone_flags, p)) goto bad_fork_cleanup_fs; - if (copy_mm(clone_flags, p)) + if (copy_signal(clone_flags, p)) goto bad_fork_cleanup_sighand; + if (copy_mm(clone_flags, p)) + goto bad_fork_cleanup_signal; if (copy_namespace(clone_flags, p)) goto bad_fork_cleanup_mm; retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); @@ -910,6 +929,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ if (sigismember(¤t->pending.signal, SIGKILL)) { write_unlock_irq(&tasklist_lock); + retval = -EINTR; goto bad_fork_cleanup_namespace; } @@ -921,20 +941,31 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->parent = p->real_parent; if (clone_flags & CLONE_THREAD) { - spin_lock(¤t->sig->siglock); + spin_lock(¤t->sighand->siglock); /* * Important: if an exit-all has been started then * do not create this new thread - the whole thread * group is supposed to exit anyway. */ - if (current->sig->group_exit) { - spin_unlock(¤t->sig->siglock); + if (current->signal->group_exit) { + spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); goto bad_fork_cleanup_namespace; } p->tgid = current->tgid; p->group_leader = current->group_leader; - spin_unlock(¤t->sig->siglock); + + if (current->signal->group_stop_count > 0) { + /* + * There is an all-stop in progress for the group. + * We ourselves will stop as soon as we check signals. + * Make the new thread part of that group stop too. + */ + current->signal->group_stop_count++; + set_tsk_thread_flag(p, TIF_SIGPENDING); + } + + spin_unlock(¤t->sighand->siglock); } SET_LINKS(p); @@ -964,6 +995,8 @@ bad_fork_cleanup_namespace: exit_namespace(p); bad_fork_cleanup_mm: exit_mm(p); +bad_fork_cleanup_signal: + exit_signal(p); bad_fork_cleanup_sighand: exit_sighand(p); bad_fork_cleanup_fs: @@ -1036,8 +1069,13 @@ struct task_struct *do_fork(unsigned long clone_flags, init_completion(&vfork); } - if (p->ptrace & PT_PTRACED) - send_sig(SIGSTOP, p, 1); + if (p->ptrace & PT_PTRACED) { + /* + * We'll start up with an immediate SIGSTOP. + */ + sigaddset(&p->pending.signal, SIGSTOP); + set_tsk_thread_flag(p, TIF_SIGPENDING); + } wake_up_forked_process(p); /* do this last */ ++total_forks; @@ -1047,9 +1085,11 @@ struct task_struct *do_fork(unsigned long clone_flags, ptrace_notify ((trace << 8) | SIGTRAP); } - if (clone_flags & CLONE_VFORK) + if (clone_flags & CLONE_VFORK) { wait_for_completion(&vfork); - else + if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) + ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); + } else /* * Let the child process run first, to avoid most of the * COW overhead when the child exec()s afterwards. @@ -1059,8 +1099,11 @@ struct task_struct *do_fork(unsigned long clone_flags, return p; } -/* SLAB cache for signal_struct structures (tsk->sig) */ -kmem_cache_t *sigact_cachep; +/* SLAB cache for signal_struct structures (tsk->signal) */ +kmem_cache_t *signal_cachep; + +/* SLAB cache for sighand_struct structures (tsk->sighand) */ +kmem_cache_t *sighand_cachep; /* SLAB cache for files_struct structures (tsk->files) */ kmem_cache_t *files_cachep; @@ -1076,11 +1119,17 @@ kmem_cache_t *mm_cachep; void __init proc_caches_init(void) { - sigact_cachep = kmem_cache_create("signal_act", + sighand_cachep = kmem_cache_create("sighand_cache", + sizeof(struct sighand_struct), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!sighand_cachep) + panic("Cannot create sighand SLAB cache"); + + signal_cachep = kmem_cache_create("signal_cache", sizeof(struct signal_struct), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!sigact_cachep) - panic("Cannot create signal action SLAB cache"); + if (!signal_cachep) + panic("Cannot create signal SLAB cache"); files_cachep = kmem_cache_create("files_cache", sizeof(struct files_struct), 0, diff --git a/kernel/kmod.c b/kernel/kmod.c index 6a9a2c8f937c..2b85eff87f43 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -111,12 +111,12 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) as the super user right after the execve fails if you time the signal just right. */ - spin_lock_irq(&curtask->sig->siglock); + spin_lock_irq(&curtask->sighand->siglock); sigemptyset(&curtask->blocked); flush_signals(curtask); flush_signal_handlers(curtask); recalc_sigpending(); - spin_unlock_irq(&curtask->sig->siglock); + spin_unlock_irq(&curtask->sighand->siglock); for (i = 0; i < curtask->files->max_fds; i++ ) { if (curtask->files->fd[i]) close(i); @@ -239,20 +239,20 @@ int request_module(const char * module_name) } /* Block everything but SIGKILL/SIGSTOP */ - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); tmpsig = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); waitpid_result = waitpid(pid, NULL, __WCLONE); atomic_dec(&kmod_concurrent); /* Allow signals again.. */ - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = tmpsig; recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); if (waitpid_result != pid) { printk(KERN_ERR "request_module[%s]: waitpid(%d,...) failed, errno %d\n", diff --git a/kernel/ptrace.c b/kernel/ptrace.c index a16dfb90d412..14d158864d9e 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -277,15 +277,43 @@ static int ptrace_setoptions(struct task_struct *child, long data) else child->ptrace &= ~PT_TRACE_EXEC; + if (data & PTRACE_O_TRACEVFORKDONE) + child->ptrace |= PT_TRACE_VFORK_DONE; + else + child->ptrace &= ~PT_TRACE_VFORK_DONE; + + if (data & PTRACE_O_TRACEEXIT) + child->ptrace |= PT_TRACE_EXIT; + else + child->ptrace &= ~PT_TRACE_EXIT; + if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE - | PTRACE_O_TRACEEXEC)) + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT + | PTRACE_O_TRACEVFORKDONE)) != data) return -EINVAL; return 0; } +static int ptrace_getsiginfo(struct task_struct *child, long data) +{ + if (child->last_siginfo == NULL) + return -EINVAL; + return copy_siginfo_to_user ((siginfo_t *) data, child->last_siginfo); +} + +static int ptrace_setsiginfo(struct task_struct *child, long data) +{ + if (child->last_siginfo == NULL) + return -EINVAL; + if (copy_from_user (child->last_siginfo, (siginfo_t *) data, + sizeof (siginfo_t)) != 0) + return -EFAULT; + return 0; +} + int ptrace_request(struct task_struct *child, long request, long addr, long data) { @@ -301,6 +329,12 @@ int ptrace_request(struct task_struct *child, long request, case PTRACE_GETEVENTMSG: ret = put_user(child->ptrace_message, (unsigned long *) data); break; + case PTRACE_GETSIGINFO: + ret = ptrace_getsiginfo(child, data); + break; + case PTRACE_SETSIGINFO: + ret = ptrace_setsiginfo(child, data); + break; default: break; } diff --git a/kernel/signal.c b/kernel/signal.c index 7c485d01a4b0..05791342da39 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -55,7 +55,7 @@ int max_queued_signals = 1024; | SIGALRM | load-balance | kill-all | | SIGTERM | load-balance | kill-all | | SIGCHLD | load-balance | ignore | -| SIGCONT | specific | continue-all | +| SIGCONT | load-balance | ignore | | SIGSTOP | n/a | stop-all | | SIGTSTP | load-balance | stop-all | | SIGTTIN | load-balance | stop-all | @@ -98,26 +98,11 @@ int max_queued_signals = 1024; #endif #if SIGRTMIN > BITS_PER_LONG -#define M(sig) (1ULL << (sig)) +#define M(sig) (1ULL << ((sig)-1)) #else -#define M(sig) (1UL << (sig)) +#define M(sig) (1UL << ((sig)-1)) #endif -#define T(sig, mask) (M(sig) & mask) - -#define SIG_USER_SPECIFIC_MASK (\ - M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | M(SIGBUS) | \ - M(SIGFPE) | M(SIGSEGV) | M(SIGPIPE) | M(SIGXFSZ) | \ - M(SIGPROF) | M(SIGSYS) | M_SIGSTKFLT | M(SIGCONT) | \ - M_SIGEMT ) - -#define SIG_USER_LOAD_BALANCE_MASK (\ - M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGUSR1) | \ - M(SIGUSR2) | M(SIGALRM) | M(SIGTERM) | M(SIGCHLD) | \ - M(SIGURG) | M(SIGVTALRM) | M(SIGPOLL) | M(SIGWINCH) | \ - M(SIGPWR) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) ) - -#define SIG_KERNEL_SPECIFIC_MASK (\ - M(SIGCHLD) | M(SIGURG) | M(SIGWINCH) ) +#define T(sig, mask) (M(sig) & (mask)) #define SIG_KERNEL_BROADCAST_MASK (\ M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \ @@ -132,34 +117,42 @@ int max_queued_signals = 1024; #define SIG_KERNEL_ONLY_MASK (\ M(SIGKILL) | M(SIGSTOP) ) +#define SIG_KERNEL_STOP_MASK (\ + M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) ) + +#define SIG_KERNEL_CONT_MASK (\ + M(SIGCONT) | M(SIGKILL) ) + #define SIG_KERNEL_COREDUMP_MASK (\ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \ M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT ) -#define sig_user_specific(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_USER_SPECIFIC_MASK)) -#define sig_user_load_balance(sig) \ - (((sig) >= SIGRTMIN) || T(sig, SIG_USER_LOAD_BALANCE_MASK)) -#define sig_kernel_specific(sig) \ - (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_SPECIFIC_MASK)) -#define sig_kernel_broadcast(sig) \ - (((sig) >= SIGRTMIN) || T(sig, SIG_KERNEL_BROADCAST_MASK)) +#define SIG_KERNEL_IGNORE_MASK (\ + M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) ) + #define sig_kernel_only(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK)) #define sig_kernel_coredump(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK)) +#define sig_kernel_ignore(sig) \ + (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK)) +#define sig_kernel_stop(sig) \ + (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK)) +#define sig_kernel_cont(sig) \ + (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_CONT_MASK)) -#define sig_user_defined(t, sig) \ - (((t)->sig->action[(sig)-1].sa.sa_handler != SIG_DFL) && \ - ((t)->sig->action[(sig)-1].sa.sa_handler != SIG_IGN)) +#define sig_user_defined(t, signr) \ + (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \ + ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) -#define sig_ignored(t, sig) \ - (((sig) != SIGCHLD) && \ - ((t)->sig->action[(sig)-1].sa.sa_handler == SIG_IGN)) +#define sig_ignored(t, signr) \ + (!((t)->ptrace & PT_PTRACED) && \ + (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_IGN) -static int -__send_sig_info(int sig, struct siginfo *info, struct task_struct *p); +#define sig_fatal(t, signr) \ + (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ + (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) /* * Re-calculate pending state from the set of locally pending @@ -193,10 +186,11 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) -void recalc_sigpending_tsk(struct task_struct *t) +inline void recalc_sigpending_tsk(struct task_struct *t) { - if (PENDING(&t->pending, &t->blocked) || - PENDING(&t->sig->shared_pending, &t->blocked)) + if (t->signal->group_stop_count > 0 || + PENDING(&t->pending, &t->blocked) || + PENDING(&t->signal->shared_pending, &t->blocked)) set_tsk_thread_flag(t, TIF_SIGPENDING); else clear_tsk_thread_flag(t, TIF_SIGPENDING); @@ -204,11 +198,7 @@ void recalc_sigpending_tsk(struct task_struct *t) void recalc_sigpending(void) { - if (PENDING(¤t->pending, ¤t->blocked) || - PENDING(¤t->sig->shared_pending, ¤t->blocked)) - set_thread_flag(TIF_SIGPENDING); - else - clear_thread_flag(TIF_SIGPENDING); + recalc_sigpending_tsk(current); } /* Given the mask, find the first available signal that should be serviced. */ @@ -280,20 +270,41 @@ flush_signals(struct task_struct *t) */ void __exit_sighand(struct task_struct *tsk) { - struct signal_struct * sig = tsk->sig; + struct sighand_struct * sighand = tsk->sighand; + + /* Ok, we're done with the signal handlers */ + tsk->sighand = NULL; + if (atomic_dec_and_test(&sighand->count)) + kmem_cache_free(sighand_cachep, sighand); +} + +void exit_sighand(struct task_struct *tsk) +{ + write_lock_irq(&tasklist_lock); + __exit_sighand(tsk); + write_unlock_irq(&tasklist_lock); +} + +/* + * This function expects the tasklist_lock write-locked. + */ +void __exit_signal(struct task_struct *tsk) +{ + struct signal_struct * sig = tsk->signal; + struct sighand_struct * sighand = tsk->sighand; if (!sig) BUG(); if (!atomic_read(&sig->count)) BUG(); - spin_lock(&sig->siglock); + spin_lock(&sighand->siglock); if (atomic_dec_and_test(&sig->count)) { if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); - tsk->sig = NULL; - spin_unlock(&sig->siglock); + tsk->signal = NULL; + spin_unlock(&sighand->siglock); flush_sigqueue(&sig->shared_pending); - kmem_cache_free(sigact_cachep, sig); + kmem_cache_free(signal_cachep, sig); } else { /* * If there is any task waiting for the group exit @@ -305,17 +316,17 @@ void __exit_sighand(struct task_struct *tsk) } if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); - tsk->sig = NULL; - spin_unlock(&sig->siglock); + tsk->signal = NULL; + spin_unlock(&sighand->siglock); } clear_tsk_thread_flag(tsk,TIF_SIGPENDING); flush_sigqueue(&tsk->pending); } -void exit_sighand(struct task_struct *tsk) +void exit_signal(struct task_struct *tsk) { write_lock_irq(&tasklist_lock); - __exit_sighand(tsk); + __exit_signal(tsk); write_unlock_irq(&tasklist_lock); } @@ -327,7 +338,7 @@ void flush_signal_handlers(struct task_struct *t) { int i; - struct k_sigaction *ka = &t->sig->action[0]; + struct k_sigaction *ka = &t->sighand->action[0]; for (i = _NSIG ; i != 0 ; i--) { if (ka->sa.sa_handler != SIG_IGN) ka->sa.sa_handler = SIG_DFL; @@ -337,23 +348,6 @@ flush_signal_handlers(struct task_struct *t) } } -/* - * sig_exit - cause the current task to exit due to a signal. - */ - -void -sig_exit(int sig, int exit_code, struct siginfo *info) -{ - sigaddset(¤t->pending.signal, sig); - recalc_sigpending(); - current->flags |= PF_SIGNALED; - - if (current->sig->group_exit) - exit_code = current->sig->group_exit_code; - - do_exit(exit_code); - /* NOTREACHED */ -} /* Notify the system that a driver wants to block all signals for this * process, and wants to be notified if any signals at all were to be @@ -368,11 +362,11 @@ block_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask) { unsigned long flags; - spin_lock_irqsave(¤t->sig->siglock, flags); + spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier_mask = mask; current->notifier_data = priv; current->notifier = notifier; - spin_unlock_irqrestore(¤t->sig->siglock, flags); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); } /* Notify the system that blocking has ended. */ @@ -382,11 +376,11 @@ unblock_all_signals(void) { unsigned long flags; - spin_lock_irqsave(¤t->sig->siglock, flags); + spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier = NULL; current->notifier_data = NULL; recalc_sigpending(); - spin_unlock_irqrestore(¤t->sig->siglock, flags); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); } static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info) @@ -473,32 +467,74 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, */ int dequeue_signal(sigset_t *mask, siginfo_t *info) { + int signr = __dequeue_signal(¤t->pending, mask, info); + if (!signr) + signr = __dequeue_signal(¤t->signal->shared_pending, + mask, info); + return signr; +} + +/* + * Tell a process that it has a new active signal.. + * + * NOTE! we rely on the previous spin_lock to + * lock interrupts for us! We can only be called with + * "siglock" held, and the local interrupt must + * have been disabled when that got acquired! + * + * No need to set need_resched since signal event passing + * goes through ->blocked + */ +inline void signal_wake_up(struct task_struct *t, int resume) +{ + set_tsk_thread_flag(t,TIF_SIGPENDING); + /* - * Here we handle shared pending signals. To implement the full - * semantics we need to unqueue and resend them. It will likely - * get into our own pending queue. + * If the task is running on a different CPU + * force a reschedule on the other CPU to make + * it notice the new signal quickly. + * + * The code below is a tad loose and might occasionally + * kick the wrong CPU if we catch the process in the + * process of changing - but no harm is done by that + * other than doing an extra (lightweight) IPI interrupt. */ - if (current->sig->shared_pending.head) { - int signr = __dequeue_signal(¤t->sig->shared_pending, mask, info); - if (signr) - __send_sig_info(signr, info, current); + if (t->state == TASK_RUNNING) + kick_if_running(t); + /* + * If resume is set, we want to wake it up in the TASK_STOPPED case. + * We don't check for TASK_STOPPED because there is a race with it + * executing another processor and just now entering stopped state. + * By calling wake_up_process any time resume is set, we ensure + * the process will wake up and handle its stop or death signal. + */ + if ((t->state & TASK_INTERRUPTIBLE) || + (resume && t->state < TASK_ZOMBIE)) { + wake_up_process(t); + return; } - return __dequeue_signal(¤t->pending, mask, info); } -static int rm_from_queue(int sig, struct sigpending *s) +/* + * Remove signals in mask from the pending set and queue. + * Returns 1 if any signals were found. + * + * All callers must be holding the siglock. + */ +static int rm_from_queue(unsigned long mask, struct sigpending *s) { struct sigqueue *q, **pp; - if (!sigismember(&s->signal, sig)) + if (!sigtestsetmask(&s->signal, mask)) return 0; - sigdelset(&s->signal, sig); + sigdelsetmask(&s->signal, mask); pp = &s->head; while ((q = *pp) != NULL) { - if (q->info.si_signo == sig) { + if (q->info.si_signo < SIGRTMIN && + (mask & sigmask (q->info.si_signo))) { if ((*pp = q->next) == NULL) s->tail = pp; kmem_cache_free(sigqueue_cachep,q); @@ -511,111 +547,109 @@ static int rm_from_queue(int sig, struct sigpending *s) } /* - * Remove signal sig from t->pending. - * Returns 1 if sig was found. - * - * All callers must be holding the siglock. - */ -static int rm_sig_from_queue(int sig, struct task_struct *t) -{ - return rm_from_queue(sig, &t->pending); -} - -/* * Bad permissions for sending the signal */ -static inline int bad_signal(int sig, struct siginfo *info, struct task_struct *t) +static inline int check_kill_permission(int sig, struct siginfo *info, + struct task_struct *t) { - return (!info || ((unsigned long)info != 1 && + int error = -EINVAL; + if (sig < 0 || sig > _NSIG) + return error; + error = -EPERM; + if ((!info || ((unsigned long)info != 1 && (unsigned long)info != 2 && SI_FROMUSER(info))) && ((sig != SIGCONT) || (current->session != t->session)) && (current->euid ^ t->suid) && (current->euid ^ t->uid) && (current->uid ^ t->suid) && (current->uid ^ t->uid) - && !capable(CAP_KILL); + && !capable(CAP_KILL)) + return error; + return security_task_kill(t, info, sig); } -/* - * Signal type: - * < 0 : global action (kill - spread to all non-blocked threads) - * = 0 : ignored - * > 0 : wake up. - */ -static int signal_type(int sig, struct signal_struct *signals) -{ - unsigned long handler; - - if (!signals) - return 0; - - handler = (unsigned long) signals->action[sig-1].sa.sa_handler; - if (handler > 1) - return 1; - - /* "Ignore" handler.. Illogical, but that has an implicit handler for SIGCHLD */ - if (handler == 1) - return sig == SIGCHLD; - - /* Default handler. Normally lethal, but.. */ - switch (sig) { - - /* Ignored */ - case SIGCONT: case SIGWINCH: - case SIGCHLD: case SIGURG: - return 0; - - /* Implicit behaviour */ - case SIGTSTP: case SIGTTIN: case SIGTTOU: - return 1; - - /* Implicit actions (kill or do special stuff) */ - default: - return -1; - } -} - +/* forward decl */ +static void do_notify_parent_cldstop(struct task_struct *tsk, + struct task_struct *parent); /* - * Determine whether a signal should be posted or not. - * - * Signals with SIG_IGN can be ignored, except for the - * special case of a SIGCHLD. - * - * Some signals with SIG_DFL default to a non-action. + * Handle magic process-wide effects of stop/continue signals, and SIGKILL. + * Unlike the signal actions, these happen immediately at signal-generation + * time regardless of blocking, ignoring, or handling. This does the + * actual continuing for SIGCONT, but not the actual stopping for stop + * signals. The process stop is done as a signal action for SIG_DFL. */ -static int ignored_signal(int sig, struct task_struct *t) +static void handle_stop_signal(int sig, struct task_struct *p) { - /* Don't ignore traced or blocked signals */ - if ((t->ptrace & PT_PTRACED) || sigismember(&t->blocked, sig)) - return 0; - - return signal_type(sig, t->sig) == 0; -} + struct task_struct *t; -/* - * Handle TASK_STOPPED cases etc implicit behaviour - * of certain magical signals. - * - * SIGKILL gets spread out to every thread. - */ -static void handle_stop_signal(int sig, struct task_struct *t) -{ - switch (sig) { - case SIGKILL: case SIGCONT: - /* Wake up the process if stopped. */ - if (t->state == TASK_STOPPED) + if (sig_kernel_stop(sig)) { + /* + * This is a stop signal. Remove SIGCONT from all queues. + */ + rm_from_queue(sigmask(SIGCONT), &p->signal->shared_pending); + t = p; + do { + rm_from_queue(sigmask(SIGCONT), &t->pending); + t = next_thread(t); + } while (t != p); + } + else if (sig_kernel_cont(sig)) { + /* + * Remove all stop signals from all queues, + * and wake all threads. + */ + if (unlikely(p->signal->group_stop_count > 0)) { + /* + * There was a group stop in progress. We'll + * pretend it finished before we got here. We are + * obliged to report it to the parent: if the + * SIGSTOP happened "after" this SIGCONT, then it + * would have cleared this pending SIGCONT. If it + * happened "before" this SIGCONT, then the parent + * got the SIGCHLD about the stop finishing before + * the continue happened. We do the notification + * now, and it's as if the stop had finished and + * the SIGCHLD was pending on entry to this kill. + */ + p->signal->group_stop_count = 0; + if (p->ptrace & PT_PTRACED) + do_notify_parent_cldstop(p, p->parent); + else + do_notify_parent_cldstop( + p->group_leader, + p->group_leader->real_parent); + } + rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); + t = p; + do { + rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); + /* + * This wakeup is only need if in TASK_STOPPED, + * but there can be SMP races with testing for that. + * In the normal SIGCONT case, all will be stopped. + * A spuriously sent SIGCONT will interrupt all running + * threads to check signals even if it's ignored. + * + * If there is a handler for SIGCONT, we must make + * sure that no thread returns to user mode before + * we post the signal, in case it was the only + * thread eligible to run the signal handler--then + * it must not do anything between resuming and + * running the handler. With the TIF_SIGPENDING + * flag set, the thread will pause and acquire the + * siglock that we hold now and until we've queued + * the pending signal. For SIGKILL, we likewise + * don't want anybody doing anything but taking the + * SIGKILL. The only case in which a thread would + * not already be in the signal dequeuing loop is + * non-signal (e.g. syscall) ptrace tracing, so we + * don't worry about an unnecessary trip through + * the signal code and just keep this code path + * simpler by unconditionally setting the flag. + */ + set_tsk_thread_flag(t, TIF_SIGPENDING); wake_up_process(t); - t->exit_code = 0; - rm_sig_from_queue(SIGSTOP, t); - rm_sig_from_queue(SIGTSTP, t); - rm_sig_from_queue(SIGTTOU, t); - rm_sig_from_queue(SIGTTIN, t); - break; - - case SIGSTOP: case SIGTSTP: - case SIGTTIN: case SIGTTOU: - /* If we're stopping again, cancel SIGCONT */ - rm_sig_from_queue(SIGCONT, t); - break; + t = next_thread(t); + } while (t != p); } } @@ -647,23 +681,23 @@ static int send_signal(int sig, struct siginfo *info, struct sigpending *signals *signals->tail = q; signals->tail = &q->next; switch ((unsigned long) info) { - case 0: - q->info.si_signo = sig; - q->info.si_errno = 0; - q->info.si_code = SI_USER; - q->info.si_pid = current->pid; - q->info.si_uid = current->uid; - break; - case 1: - q->info.si_signo = sig; - q->info.si_errno = 0; - q->info.si_code = SI_KERNEL; - q->info.si_pid = 0; - q->info.si_uid = 0; - break; - default: - copy_siginfo(&q->info, info); - break; + case 0: + q->info.si_signo = sig; + q->info.si_errno = 0; + q->info.si_code = SI_USER; + q->info.si_pid = current->pid; + q->info.si_uid = current->uid; + break; + case 1: + q->info.si_signo = sig; + q->info.si_errno = 0; + q->info.si_code = SI_KERNEL; + q->info.si_pid = 0; + q->info.si_uid = 0; + break; + default: + copy_siginfo(&q->info, info); + break; } } else if (sig >= SIGRTMIN && info && (unsigned long)info != 1 && info->si_code != SI_USER) @@ -678,103 +712,36 @@ out_set: return 0; } -/* - * Tell a process that it has a new active signal.. - * - * NOTE! we rely on the previous spin_lock to - * lock interrupts for us! We can only be called with - * "siglock" held, and the local interrupt must - * have been disabled when that got acquired! - * - * No need to set need_resched since signal event passing - * goes through ->blocked - */ -inline void signal_wake_up(struct task_struct *t) -{ - set_tsk_thread_flag(t,TIF_SIGPENDING); - - /* - * If the task is running on a different CPU - * force a reschedule on the other CPU to make - * it notice the new signal quickly. - * - * The code below is a tad loose and might occasionally - * kick the wrong CPU if we catch the process in the - * process of changing - but no harm is done by that - * other than doing an extra (lightweight) IPI interrupt. - */ - if (t->state == TASK_RUNNING) - kick_if_running(t); - if (t->state & TASK_INTERRUPTIBLE) { - wake_up_process(t); - return; - } -} - -static int deliver_signal(int sig, struct siginfo *info, struct task_struct *t) -{ - int retval = send_signal(sig, info, &t->pending); +#define LEGACY_QUEUE(sigptr, sig) \ + (((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig))) - if (!retval && !sigismember(&t->blocked, sig)) - signal_wake_up(t); - - return retval; -} static int -specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t, int shared) +specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t) { int ret; if (!irqs_disabled()) BUG(); #if CONFIG_SMP - if (!spin_is_locked(&t->sig->siglock)) + if (!spin_is_locked(&t->sighand->siglock)) BUG(); #endif - ret = -EINVAL; - if (sig < 0 || sig > _NSIG) - goto out; - /* The somewhat baroque permissions check... */ - ret = -EPERM; - if (bad_signal(sig, info, t)) - goto out; - ret = security_task_kill(t, info, sig); - if (ret) - goto out; - /* The null signal is a permissions and process existence probe. - No signal is actually delivered. Same goes for zombies. */ - ret = 0; - if (!sig || !t->sig) - goto out; - - handle_stop_signal(sig, t); - - /* Optimize away the signal, if it's a signal that can be - handled immediately (ie non-blocked and untraced) and - that is ignored (either explicitly or by default). */ - - if (ignored_signal(sig, t)) - goto out; + /* Short-circuit ignored signals. */ + if (sig_ignored(t, sig)) + return 0; -#define LEGACY_QUEUE(sigptr, sig) \ - (((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig))) + /* Support queueing exactly one non-rt signal, so that we + can get more detailed information about the cause of + the signal. */ + if (LEGACY_QUEUE(&t->pending, sig)) + return 0; - if (!shared) { - /* Support queueing exactly one non-rt signal, so that we - can get more detailed information about the cause of - the signal. */ - if (LEGACY_QUEUE(&t->pending, sig)) - goto out; + ret = send_signal(sig, info, &t->pending); + if (!ret && !sigismember(&t->blocked, sig)) + signal_wake_up(t, sig == SIGKILL); - ret = deliver_signal(sig, info, t); - } else { - if (LEGACY_QUEUE(&t->sig->shared_pending, sig)) - goto out; - ret = send_signal(sig, info, &t->sig->shared_pending); - } -out: return ret; } @@ -789,192 +756,202 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t) unsigned long int flags; int ret; - spin_lock_irqsave(&t->sig->siglock, flags); - if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN) - t->sig->action[sig-1].sa.sa_handler = SIG_DFL; + spin_lock_irqsave(&t->sighand->siglock, flags); + if (t->sighand->action[sig-1].sa.sa_handler == SIG_IGN) + t->sighand->action[sig-1].sa.sa_handler = SIG_DFL; sigdelset(&t->blocked, sig); recalc_sigpending_tsk(t); - ret = __send_sig_info(sig, info, t); - spin_unlock_irqrestore(&t->sig->siglock, flags); + ret = specific_send_sig_info(sig, info, t); + spin_unlock_irqrestore(&t->sighand->siglock, flags); return ret; } -static int -__specific_force_sig_info(int sig, struct task_struct *t) -{ - if (!t->sig) - return -ESRCH; - - if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN) - t->sig->action[sig-1].sa.sa_handler = SIG_DFL; - sigdelset(&t->blocked, sig); - recalc_sigpending_tsk(t); - - return specific_send_sig_info(sig, (void *)2, t, 0); -} - void force_sig_specific(int sig, struct task_struct *t) { unsigned long int flags; - spin_lock_irqsave(&t->sig->siglock, flags); - if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN) - t->sig->action[sig-1].sa.sa_handler = SIG_DFL; + spin_lock_irqsave(&t->sighand->siglock, flags); + if (t->sighand->action[sig-1].sa.sa_handler == SIG_IGN) + t->sighand->action[sig-1].sa.sa_handler = SIG_DFL; sigdelset(&t->blocked, sig); recalc_sigpending_tsk(t); - specific_send_sig_info(sig, (void *)2, t, 0); - spin_unlock_irqrestore(&t->sig->siglock, flags); + specific_send_sig_info(sig, (void *)2, t); + spin_unlock_irqrestore(&t->sighand->siglock, flags); } -#define can_take_signal(p, sig) \ - (((unsigned long) p->sig->action[sig-1].sa.sa_handler > 1) && \ - !sigismember(&p->blocked, sig) && (task_curr(p) || !signal_pending(p))) +/* + * Test if P wants to take SIG. After we've checked all threads with this, + * it's equivalent to finding no threads not blocking SIG. Any threads not + * blocking SIG were ruled out because they are not running and already + * have pending signals. Such threads will dequeue from the shared queue + * as soon as they're available, so putting the signal on the shared queue + * will be equivalent to sending it to one such thread. + */ +#define wants_signal(sig, p) (!sigismember(&(p)->blocked, sig) \ + && (p)->state < TASK_STOPPED \ + && !((p)->flags & PF_EXITING) \ + && (task_curr(p) || !signal_pending(p))) -static inline -int load_balance_thread_group(struct task_struct *p, int sig, - struct siginfo *info) +static inline int +__group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { - struct task_struct *tmp; + struct task_struct *t; int ret; +#if CONFIG_SMP + if (!spin_is_locked(&p->sighand->siglock)) + BUG(); +#endif + handle_stop_signal(sig, p); + + /* Short-circuit ignored signals. */ + if (sig_ignored(p, sig)) + return 0; + + if (LEGACY_QUEUE(&p->signal->shared_pending, sig)) + /* This is a non-RT signal and we already have one queued. */ + return 0; + /* - * if the specified thread is not blocking this signal - * then deliver it. + * Put this signal on the shared-pending queue, or fail with EAGAIN. + * We always use the shared queue for process-wide signals, + * to avoid several races. */ - if (can_take_signal(p, sig)) - return specific_send_sig_info(sig, info, p, 0); + ret = send_signal(sig, info, &p->signal->shared_pending); + if (unlikely(ret)) + return ret; /* - * Otherwise try to find a suitable thread. - * If no such thread is found then deliver to - * the original thread. + * Now find a thread we can wake up to take the signal off the queue. + * + * If the main thread wants the signal, it gets first crack. + * Probably the least surprising to the average bear. */ - - tmp = p->sig->curr_target; - - if (!tmp || tmp->tgid != p->tgid) - /* restart balancing at this thread */ - p->sig->curr_target = p; - - else for (;;) { - if (thread_group_empty(p)) - BUG(); - if (!tmp || tmp->tgid != p->tgid) - BUG(); - + if (wants_signal(sig, p)) + t = p; + else if (thread_group_empty(p)) /* - * Do not send signals that are ignored or blocked, - * or to not-running threads that are overworked: + * There is just one thread and it does not need to be woken. + * It will dequeue unblocked signals before it runs again. */ - if (!can_take_signal(tmp, sig)) { - tmp = next_thread(tmp); - p->sig->curr_target = tmp; - if (tmp == p) - break; - continue; + return 0; + else { + /* + * Otherwise try to find a suitable thread. + */ + t = p->signal->curr_target; + if (t == NULL) + /* restart balancing at this thread */ + t = p->signal->curr_target = p; + BUG_ON(t->tgid != p->tgid); + + while (!wants_signal(sig, t)) { + t = next_thread(t); + if (t == p->signal->curr_target) + /* + * No thread needs to be woken. + * Any eligible threads will see + * the signal in the queue soon. + */ + return 0; } - ret = specific_send_sig_info(sig, info, tmp, 0); - return ret; + p->signal->curr_target = t; } + /* - * No suitable thread was found - put the signal - * into the shared-pending queue. + * Found a killable thread. If the signal will be fatal, + * then start taking the whole group down immediately. */ - return specific_send_sig_info(sig, info, p, 1); -} - -int __broadcast_thread_group(struct task_struct *p, int sig) -{ - struct task_struct *tmp; - struct list_head *l; - struct pid *pid; - int err = 0; - - for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid) - err = __specific_force_sig_info(sig, tmp); - - return err; -} + if (sig_fatal(p, sig) && !p->signal->group_exit && + !sigismember(&t->real_blocked, sig) && + (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) { + /* + * This signal will be fatal to the whole group. + */ + if (!sig_kernel_coredump(sig)) { + /* + * Start a group exit and wake everybody up. + * This way we don't have other threads + * running and doing things after a slower + * thread has the fatal signal pending. + */ + p->signal->group_exit = 1; + p->signal->group_exit_code = sig; + p->signal->group_stop_count = 0; + t = p; + do { + sigaddset(&t->pending.signal, SIGKILL); + signal_wake_up(t, 1); + t = next_thread(t); + } while (t != p); + return 0; + } -struct task_struct * find_unblocked_thread(struct task_struct *p, int signr) -{ - struct task_struct *tmp; - struct list_head *l; - struct pid *pid; + /* + * There will be a core dump. We make all threads other + * than the chosen one go into a group stop so that nothing + * happens until it gets scheduled, takes the signal off + * the shared queue, and does the core dump. This is a + * little more complicated than strictly necessary, but it + * keeps the signal state that winds up in the core dump + * unchanged from the death state, e.g. which thread had + * the core-dump signal unblocked. + */ + rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); + rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); + p->signal->group_stop_count = 0; + p->signal->group_exit_task = t; + t = p; + do { + p->signal->group_stop_count++; + signal_wake_up(t, 0); + t = next_thread(t); + } while (t != p); + wake_up_process(p->signal->group_exit_task); + return 0; + } - for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid) - if (!sigismember(&tmp->blocked, signr)) - return tmp; - return NULL; + /* + * The signal is already in the shared-pending queue. + * Tell the chosen thread to wake up and dequeue it. + */ + signal_wake_up(t, sig == SIGKILL); + return 0; } -static int -__send_sig_info(int sig, struct siginfo *info, struct task_struct *p) +/* + * Nuke all other threads in the group. + */ +void zap_other_threads(struct task_struct *p) { struct task_struct *t; - int ret = 0; -#if CONFIG_SMP - if (!spin_is_locked(&p->sig->siglock)) - BUG(); -#endif - /* not a thread group - normal signal behavior */ - if (thread_group_empty(p) || !sig) - goto out_send; - - if (sig_user_defined(p, sig)) { - if (sig_user_specific(sig)) - goto out_send; - if (sig_user_load_balance(sig)) { - ret = load_balance_thread_group(p, sig, info); - goto out_unlock; - } + p->signal->group_stop_count = 0; - /* must not happen */ - BUG(); - } - /* optimize away ignored signals: */ - if (sig_ignored(p, sig)) - goto out_unlock; - - if (sig_kernel_specific(sig) || - ((p->ptrace & PT_PTRACED) && !sig_kernel_only(sig))) - goto out_send; + if (thread_group_empty(p)) + return; - /* Does any of the threads unblock the signal? */ - t = find_unblocked_thread(p, sig); - if (!t) { - ret = specific_send_sig_info(sig, info, p, 1); - goto out_unlock; + for (t = next_thread(p); t != p; t = next_thread(t)) { + sigaddset(&t->pending.signal, SIGKILL); + rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); + signal_wake_up(t, 1); } - if (sigismember(&t->real_blocked,sig)) { - ret = specific_send_sig_info(sig, info, t, 0); - goto out_unlock; - } - if (sig_kernel_broadcast(sig) || sig_kernel_coredump(sig)) { - ret = __broadcast_thread_group(p, sig); - goto out_unlock; - } - - /* must not happen */ - BUG(); -out_send: - ret = specific_send_sig_info(sig, info, p, 0); -out_unlock: - return ret; } int -send_sig_info(int sig, struct siginfo *info, struct task_struct *p) +group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { unsigned long flags; int ret; - spin_lock_irqsave(&p->sig->siglock, flags); - ret = __send_sig_info(sig, info, p); - spin_unlock_irqrestore(&p->sig->siglock, flags); + ret = check_kill_permission(sig, info, p); + if (!ret && sig && p->sighand) { + spin_lock_irqsave(&p->sighand->siglock, flags); + ret = __group_send_sig_info(sig, info, p); + spin_unlock_irqrestore(&p->sighand->siglock, flags); + } return ret; } @@ -995,7 +972,7 @@ int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) return -EINVAL; for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { - err = send_sig_info(sig, info, p); + err = group_send_sig_info(sig, info, p); if (retval) retval = err; } @@ -1037,7 +1014,7 @@ kill_sl_info(int sig, struct siginfo *info, pid_t sid) for_each_task_pid(sid, PIDTYPE_SID, p, l, pid) { if (!p->leader) continue; - err = send_sig_info(sig, info, p); + err = group_send_sig_info(sig, info, p); if (retval) retval = err; } @@ -1056,7 +1033,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid) p = find_task_by_pid(pid); error = -ESRCH; if (p) - error = send_sig_info(sig, info, p); + error = group_send_sig_info(sig, info, p); read_unlock(&tasklist_lock); return error; } @@ -1079,8 +1056,8 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) read_lock(&tasklist_lock); for_each_process(p) { - if (p->pid > 1 && p != current) { - int err = send_sig_info(sig, info, p); + if (p->pid > 1 && p->tgid != current->tgid) { + int err = group_send_sig_info(sig, info, p); ++count; if (err != -EPERM) retval = err; @@ -1100,6 +1077,22 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) */ int +send_sig_info(int sig, struct siginfo *info, struct task_struct *p) +{ + /* XXX should nix these interfaces and update the kernel */ + if (T(sig, SIG_KERNEL_BROADCAST_MASK)) + /* XXX do callers really always hold the tasklist_lock?? */ + return group_send_sig_info(sig, info, p); + else { + int error; + spin_lock_irq(&p->sighand->siglock); + error = specific_send_sig_info(sig, info, p); + spin_unlock_irq(&p->sighand->siglock); + return error; + } +} + +int send_sig(int sig, struct task_struct *p, int priv) { return send_sig_info(sig, (void*)(long)(priv != 0), p); @@ -1133,9 +1126,10 @@ kill_proc(pid_t pid, int sig, int priv) * Joy. Or not. Pthread wants us to wake up every thread * in our parent group. */ -static inline void __wake_up_parent(struct task_struct *p) +static inline void __wake_up_parent(struct task_struct *p, + struct task_struct *parent) { - struct task_struct *parent = p->parent, *tsk = parent; + struct task_struct *tsk = parent; /* * Fortunately this is not necessary for thread groups: @@ -1148,7 +1142,7 @@ static inline void __wake_up_parent(struct task_struct *p) do { wake_up_interruptible(&tsk->wait_chldexit); tsk = next_thread(tsk); - if (tsk->sig != parent->sig) + if (tsk->signal != parent->signal) BUG(); } while (tsk != parent); } @@ -1162,6 +1156,7 @@ void do_notify_parent(struct task_struct *tsk, int sig) struct siginfo info; unsigned long flags; int why, status; + struct sighand_struct *psig; if (sig == -1) BUG(); @@ -1200,10 +1195,34 @@ void do_notify_parent(struct task_struct *tsk, int sig) info.si_code = why; info.si_status = status; - spin_lock_irqsave(&tsk->parent->sig->siglock, flags); - __send_sig_info(sig, &info, tsk->parent); - __wake_up_parent(tsk); - spin_unlock_irqrestore(&tsk->parent->sig->siglock, flags); + psig = tsk->parent->sighand; + spin_lock_irqsave(&psig->siglock, flags); + if (sig == SIGCHLD && tsk->state != TASK_STOPPED && + (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN || + (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) { + /* + * We are exiting and our parent doesn't care. POSIX.1 + * defines special semantics for setting SIGCHLD to SIG_IGN + * or setting the SA_NOCLDWAIT flag: we should be reaped + * automatically and not left for our parent's wait4 call. + * Rather than having the parent do it as a magic kind of + * signal handler, we just set this to tell do_exit that we + * can be cleaned up without becoming a zombie. Note that + * we still call __wake_up_parent in this case, because a + * blocked sys_wait4 might now return -ECHILD. + * + * Whether we send SIGCHLD or not for SA_NOCLDWAIT + * is implementation-defined: we do (if you don't want + * it, just use SIG_IGN instead). + */ + tsk->exit_signal = -1; + if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) + sig = 0; + } + if (sig > 0 && sig <= _NSIG) + __group_send_sig_info(sig, &info, tsk->parent); + __wake_up_parent(tsk, tsk->parent); + spin_unlock_irqrestore(&psig->siglock, flags); } @@ -1224,6 +1243,152 @@ notify_parent(struct task_struct *tsk, int sig) } } +static void +do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) +{ + struct siginfo info; + unsigned long flags; + struct sighand_struct *sighand; + + info.si_signo = SIGCHLD; + info.si_errno = 0; + info.si_pid = tsk->pid; + info.si_uid = tsk->uid; + + /* FIXME: find out whether or not this is supposed to be c*time. */ + info.si_utime = tsk->utime; + info.si_stime = tsk->stime; + + info.si_status = tsk->exit_code & 0x7f; + info.si_code = CLD_STOPPED; + + sighand = parent->sighand; + spin_lock_irqsave(&sighand->siglock, flags); + if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN && + !(sighand->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + __group_send_sig_info(SIGCHLD, &info, parent); + /* + * Even if SIGCHLD is not generated, we must wake up wait4 calls. + */ + __wake_up_parent(tsk, parent); + spin_unlock_irqrestore(&sighand->siglock, flags); +} + +static void +finish_stop(int stop_count) +{ + /* + * If there are no other threads in the group, or if there is + * a group stop in progress and we are the last to stop, + * report to the parent. When ptraced, every thread reports itself. + */ + if (stop_count < 0 || (current->ptrace & PT_PTRACED)) { + read_lock(&tasklist_lock); + do_notify_parent_cldstop(current, current->parent); + read_unlock(&tasklist_lock); + } + else if (stop_count == 0) { + read_lock(&tasklist_lock); + do_notify_parent_cldstop(current->group_leader, + current->group_leader->real_parent); + read_unlock(&tasklist_lock); + } + + schedule(); + /* + * Now we don't run again until continued. + */ + current->exit_code = 0; +} + +/* + * This performs the stopping for SIGSTOP and other stop signals. + * We have to stop all threads in the thread group. + */ +static void +do_signal_stop(int signr) +{ + struct signal_struct *sig = current->signal; + struct sighand_struct *sighand = current->sighand; + int stop_count = -1; + + if (sig->group_stop_count > 0) { + /* + * There is a group stop in progress. We don't need to + * start another one. + */ + spin_lock_irq(&sighand->siglock); + if (unlikely(sig->group_stop_count == 0)) { + BUG_ON(!sig->group_exit); + spin_unlock_irq(&sighand->siglock); + return; + } + signr = sig->group_exit_code; + stop_count = --sig->group_stop_count; + current->exit_code = signr; + set_current_state(TASK_STOPPED); + spin_unlock_irq(&sighand->siglock); + } + else if (thread_group_empty(current)) { + /* + * No locks needed in this case. + */ + current->exit_code = signr; + set_current_state(TASK_STOPPED); + } + else { + /* + * There is no group stop already in progress. + * We must initiate one now. + */ + struct task_struct *t; + read_lock(&tasklist_lock); + spin_lock_irq(&sighand->siglock); + + if (unlikely(sig->group_exit)) { + /* + * There is a group exit in progress now. + * We'll just ignore the stop and process the + * associated fatal signal. + */ + spin_unlock_irq(&sighand->siglock); + read_unlock(&tasklist_lock); + return; + } + + if (sig->group_stop_count == 0) { + sig->group_exit_code = signr; + stop_count = 0; + for (t = next_thread(current); t != current; + t = next_thread(t)) + /* + * Setting state to TASK_STOPPED for a group + * stop is always done with the siglock held, + * so this check has no races. + */ + if (t->state < TASK_STOPPED) { + stop_count++; + signal_wake_up(t, 0); + } + sig->group_stop_count = stop_count; + } + else { + /* A race with another thread while unlocked. */ + signr = sig->group_exit_code; + stop_count = --sig->group_stop_count; + } + + current->exit_code = signr; + set_current_state(TASK_STOPPED); + + spin_unlock_irq(&sighand->siglock); + read_unlock(&tasklist_lock); + } + + finish_stop(stop_count); +} + + #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) @@ -1234,31 +1399,65 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) unsigned long signr = 0; struct k_sigaction *ka; - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); + if (unlikely(current->signal->group_stop_count > 0)) { + int stop_count; + if (current->signal->group_exit_task == current) { + /* + * Group stop is so we can do a core dump. + */ + current->signal->group_exit_task = NULL; + goto dequeue; + } + /* + * There is a group stop in progress. We stop + * without any associated signal being in our queue. + */ + stop_count = --current->signal->group_stop_count; + signr = current->signal->group_exit_code; + current->exit_code = signr; + set_current_state(TASK_STOPPED); + spin_unlock_irq(¤t->sighand->siglock); + finish_stop(stop_count); + continue; + } + dequeue: signr = dequeue_signal(mask, info); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); if (!signr) break; if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* + * If there is a group stop in progress, + * we must participate in the bookkeeping. + */ + if (current->signal->group_stop_count > 0) { + spin_lock_irq(¤t->sighand->siglock); + --current->signal->group_stop_count; + spin_unlock_irq(¤t->sighand->siglock); + } + /* Let the debugger run. */ current->exit_code = signr; + current->last_siginfo = info; set_current_state(TASK_STOPPED); notify_parent(current, SIGCHLD); schedule(); + current->last_siginfo = NULL; + /* We're back. Did the debugger cancel the sig? */ signr = current->exit_code; if (signr == 0) continue; current->exit_code = 0; - /* The debugger continued. Ignore SIGSTOP. */ - if (signr == SIGSTOP) - continue; - - /* Update the siginfo structure. Is this good? */ + /* Update the siginfo structure if the signal has + changed. If the debugger wanted something + specific in the siginfo structure then it should + have updated *info via PTRACE_SETSIGINFO. */ if (signr != info->si_signo) { info->si_signo = signr; info->si_errno = 0; @@ -1269,61 +1468,69 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) /* If the (new) signal is now blocked, requeue it. */ if (sigismember(¤t->blocked, signr)) { - send_sig_info(signr, info, current); + spin_lock_irq(¤t->sighand->siglock); + specific_send_sig_info(signr, info, current); + spin_unlock_irq(¤t->sighand->siglock); continue; } } - ka = ¤t->sig->action[signr-1]; - if (ka->sa.sa_handler == SIG_IGN) { - if (signr != SIGCHLD) - continue; - /* Check for SIGCHLD: it's special. */ - while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) - /* nothing */; + ka = ¤t->sighand->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */ continue; - } - - if (ka->sa.sa_handler == SIG_DFL) { - int exit_code = signr; - - /* Init gets no signals it doesn't want. */ - if (current->pid == 1) - continue; + if (ka->sa.sa_handler != SIG_DFL) /* Run the handler. */ + return signr; - switch (signr) { - case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG: - continue; + /* + * Now we are doing the default action for this signal. + */ + if (sig_kernel_ignore(signr)) /* Default is nothing. */ + continue; - case SIGTSTP: case SIGTTIN: case SIGTTOU: - if (is_orphaned_pgrp(current->pgrp)) - continue; - /* FALLTHRU */ - - case SIGSTOP: { - struct signal_struct *sig; - set_current_state(TASK_STOPPED); - current->exit_code = signr; - sig = current->parent->sig; - if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) - notify_parent(current, SIGCHLD); - schedule(); - continue; - } + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; - case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGABRT: case SIGFPE: case SIGSEGV: - case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: - if (do_coredump(signr, exit_code, regs)) - exit_code |= 0x80; - /* FALLTHRU */ + if (sig_kernel_stop(signr)) { + /* + * The default action is to stop all threads in + * the thread group. The job control signals + * do nothing in an orphaned pgrp, but SIGSTOP + * always works. + */ + if (signr == SIGSTOP || + !is_orphaned_pgrp(current->pgrp)) + do_signal_stop(signr); + continue; + } - default: - sig_exit(signr, exit_code, info); + /* + * Anything else is fatal, maybe with a core dump. + */ + current->flags |= PF_SIGNALED; + if (sig_kernel_coredump(signr) && + do_coredump(signr, signr, regs)) { + /* + * That killed all other threads in the group and + * synchronized with their demise, so there can't + * be any more left to kill now. The group_exit + * flags are set by do_coredump. Note that + * thread_group_empty won't always be true yet, + * because those threads were blocked in __exit_mm + * and we just let them go to finish dying. + */ + const int code = signr | 0x80; + BUG_ON(!current->signal->group_exit); + BUG_ON(current->signal->group_exit_code != code); + do_exit(code); /* NOTREACHED */ } - } - return signr; + + /* + * Death signals, no core dump. + */ + do_group_exit(signr); + /* NOTREACHED */ } return 0; } @@ -1386,7 +1593,7 @@ sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize) goto out; sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); old_set = current->blocked; error = 0; @@ -1406,15 +1613,15 @@ sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize) current->blocked = new_set; recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); if (error) goto out; if (oset) goto set_old; } else if (oset) { - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); old_set = current->blocked; - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); set_old: error = -EFAULT; @@ -1434,13 +1641,18 @@ long do_sigpending(void *set, unsigned long sigsetsize) if (sigsetsize > sizeof(sigset_t)) goto out; - spin_lock_irq(¤t->sig->siglock); - sigandsets(&pending, ¤t->blocked, ¤t->pending.signal); - spin_unlock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); + sigorsets(&pending, ¤t->pending.signal, + ¤t->signal->shared_pending.signal); + spin_unlock_irq(¤t->sighand->siglock); + + /* Outside the lock because only this thread touches it. */ + sigandsets(&pending, ¤t->blocked, &pending); error = -EFAULT; if (!copy_to_user(set, &pending, sigsetsize)) error = 0; + out: return error; } @@ -1546,7 +1758,7 @@ sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, return -EINVAL; } - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); sig = dequeue_signal(&these, &info); if (!sig) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -1561,19 +1773,19 @@ sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, current->real_blocked = current->blocked; sigandsets(¤t->blocked, ¤t->blocked, &these); recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); current->state = TASK_INTERRUPTIBLE; timeout = schedule_timeout(timeout); - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); sig = dequeue_signal(&these, &info); current->blocked = current->real_blocked; siginitset(¤t->real_blocked, 0); recalc_sigpending(); } } - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); if (sig) { ret = sig; @@ -1628,9 +1840,17 @@ sys_tkill(int pid, int sig) p = find_task_by_pid(pid); error = -ESRCH; if (p) { - spin_lock_irq(&p->sig->siglock); - error = specific_send_sig_info(sig, &info, p, 0); - spin_unlock_irq(&p->sig->siglock); + error = check_kill_permission(sig, &info, p); + /* + * The null signal is a permissions and process existence + * probe. No signal is actually delivered. + */ + if (!error && sig && p->sighand) { + spin_lock_irq(&p->sighand->siglock); + handle_stop_signal(sig, p); + error = specific_send_sig_info(sig, &info, p); + spin_unlock_irq(&p->sighand->siglock); + } } read_unlock(&tasklist_lock); return error; @@ -1662,17 +1882,22 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact) if (sig < 1 || sig > _NSIG || (act && sig_kernel_only(sig))) return -EINVAL; - k = ¤t->sig->action[sig-1]; + k = ¤t->sighand->action[sig-1]; - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); + if (signal_pending(current)) { + /* + * If there might be a fatal signal pending on multiple + * threads, make sure we take it before changing the action. + */ + spin_unlock_irq(¤t->sighand->siglock); + return -ERESTARTNOINTR; + } if (oact) *oact = *k; if (act) { - *k = *act; - sigdelsetmask(&k->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP)); - /* * POSIX 3.3.1.3: * "Setting a signal action to SIG_IGN for a signal that is @@ -1683,25 +1908,40 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact) * pending and whose default action is to ignore the signal * (for example, SIGCHLD), shall cause the pending signal to * be discarded, whether or not it is blocked" - * - * Note the silly behaviour of SIGCHLD: SIG_IGN means that the - * signal isn't actually ignored, but does automatic child - * reaping, while SIG_DFL is explicitly said by POSIX to force - * the signal to be ignored. */ - - if (k->sa.sa_handler == SIG_IGN - || (k->sa.sa_handler == SIG_DFL - && (sig == SIGCONT || - sig == SIGCHLD || - sig == SIGWINCH || - sig == SIGURG))) { - if (rm_sig_from_queue(sig, current)) - recalc_sigpending(); + if (act->sa.sa_handler == SIG_IGN || + (act->sa.sa_handler == SIG_DFL && + sig_kernel_ignore(sig))) { + /* + * This is a fairly rare case, so we only take the + * tasklist_lock once we're sure we'll need it. + * Now we must do this little unlock and relock + * dance to maintain the lock hierarchy. + */ + struct task_struct *t = current; + spin_unlock_irq(&t->sighand->siglock); + read_lock(&tasklist_lock); + spin_lock_irq(&t->sighand->siglock); + *k = *act; + sigdelsetmask(&k->sa.sa_mask, + sigmask(SIGKILL) | sigmask(SIGSTOP)); + rm_from_queue(sigmask(sig), &t->signal->shared_pending); + do { + rm_from_queue(sigmask(sig), &t->pending); + recalc_sigpending_tsk(t); + t = next_thread(t); + } while (t != current); + spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); + return 0; } + + *k = *act; + sigdelsetmask(&k->sa.sa_mask, + sigmask(SIGKILL) | sigmask(SIGSTOP)); } - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); return 0; } @@ -1730,7 +1970,7 @@ do_sigaltstack (const stack_t *uss, stack_t *uoss, unsigned long sp) goto out; error = -EPERM; - if (on_sig_stack (sp)) + if (on_sig_stack(sp)) goto out; error = -EINVAL; @@ -1788,9 +2028,9 @@ sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset) error = -EFAULT; if (copy_from_user(&new_set, set, sizeof(*set))) goto out; - new_set &= ~(sigmask(SIGKILL)|sigmask(SIGSTOP)); + new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP)); - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); old_set = current->blocked.sig[0]; error = 0; @@ -1810,7 +2050,7 @@ sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset) } recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); if (error) goto out; if (oset) @@ -1872,13 +2112,13 @@ sys_ssetmask(int newmask) { int old; - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); old = current->blocked.sig[0]; siginitset(¤t->blocked, newmask & ~(sigmask(SIGKILL)| sigmask(SIGSTOP))); recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); return old; } diff --git a/kernel/suspend.c b/kernel/suspend.c index 3c55c284b537..8ed7bde5aa18 100644 --- a/kernel/suspend.c +++ b/kernel/suspend.c @@ -65,7 +65,6 @@ #include <asm/pgtable.h> #include <asm/io.h> -extern void signal_wake_up(struct task_struct *t); extern int sys_sync(void); unsigned char software_suspend_enabled = 0; @@ -219,9 +218,9 @@ int freeze_processes(void) /* FIXME: smp problem here: we may not access other process' flags without locking */ p->flags |= PF_FREEZE; - spin_lock_irqsave(&p->sig->siglock, flags); - signal_wake_up(p); - spin_unlock_irqrestore(&p->sig->siglock, flags); + spin_lock_irqsave(&p->sighand->siglock, flags); + signal_wake_up(p, 0); + spin_unlock_irqrestore(&p->sighand->siglock, flags); todo++; } while_each_thread(g, p); read_unlock(&tasklist_lock); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 156583c7dbf7..8fd97c6764a4 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -177,12 +177,13 @@ static int worker_thread(void *__startup) current->flags |= PF_IOTHREAD; cwq->thread = current; + set_user_nice(current, -10); set_cpus_allowed(current, 1UL << cpu); - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); siginitsetinv(¤t->blocked, sigmask(SIGCHLD)); recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); complete(&startup->done); @@ -212,10 +213,10 @@ static int worker_thread(void *__startup) /* SIGCHLD - auto-reaping */ ; /* zap all other signals */ - spin_lock_irq(¤t->sig->siglock); + spin_lock_irq(¤t->sighand->siglock); flush_signals(current); recalc_sigpending(); - spin_unlock_irq(¤t->sig->siglock); + spin_unlock_irq(¤t->sighand->siglock); } } remove_wait_queue(&cwq->more_work, &wait); |
