Commit 5dd6a6e5 authored by Ingo Molnar's avatar Ingo Molnar

[PATCH] exit-fix-2.5.38-E3

This fixes a number of bugs in the thread-release code:

 - notify parents only if the group leader is a zombie,
   and if it's not a detached thread.

 - do not reparent children to zombie tasks.

 - introduce the TASK_DEAD state for tasks, to serialize the task-release
   path. (to some it might be confusing that tasks are zombies first, then
   dead :-)

 - simplify tasklist_lock usage in release_task().

the effect of the above bugs ranged from unkillable hung zombies to kernel
crashes. None of those happens with the patch applied.
parent 2684cd69
...@@ -100,8 +100,9 @@ extern unsigned long nr_uninterruptible(void); ...@@ -100,8 +100,9 @@ extern unsigned long nr_uninterruptible(void);
#define TASK_RUNNING 0 #define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1 #define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2 #define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE 4 #define TASK_STOPPED 4
#define TASK_STOPPED 8 #define TASK_ZOMBIE 8
#define TASK_DEAD 16
#define __set_task_state(tsk, state_value) \ #define __set_task_state(tsk, state_value) \
do { (tsk)->state = (state_value); } while (0) do { (tsk)->state = (state_value); } while (0)
......
...@@ -32,6 +32,7 @@ int getrusage(struct task_struct *, int, struct rusage *); ...@@ -32,6 +32,7 @@ int getrusage(struct task_struct *, int, struct rusage *);
static struct dentry * __unhash_process(struct task_struct *p) static struct dentry * __unhash_process(struct task_struct *p)
{ {
struct dentry *proc_dentry; struct dentry *proc_dentry;
nr_threads--; nr_threads--;
detach_pid(p, PIDTYPE_PID); detach_pid(p, PIDTYPE_PID);
detach_pid(p, PIDTYPE_TGID); detach_pid(p, PIDTYPE_TGID);
...@@ -57,31 +58,31 @@ static struct dentry * __unhash_process(struct task_struct *p) ...@@ -57,31 +58,31 @@ static struct dentry * __unhash_process(struct task_struct *p)
void release_task(struct task_struct * p) void release_task(struct task_struct * p)
{ {
struct dentry *proc_dentry; struct dentry *proc_dentry;
task_t *leader;
if (p->state != TASK_ZOMBIE) if (p->state < TASK_ZOMBIE)
BUG(); BUG();
if (p != current) if (p != current)
wait_task_inactive(p); wait_task_inactive(p);
atomic_dec(&p->user->processes); atomic_dec(&p->user->processes);
security_ops->task_free_security(p); security_ops->task_free_security(p);
free_uid(p->user); free_uid(p->user);
if (unlikely(p->ptrace)) {
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
if (unlikely(p->ptrace))
__ptrace_unlink(p); __ptrace_unlink(p);
write_unlock_irq(&tasklist_lock);
}
BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children));
write_lock_irq(&tasklist_lock);
__exit_sighand(p); __exit_sighand(p);
proc_dentry = __unhash_process(p); proc_dentry = __unhash_process(p);
/* /*
* If we are the last non-leader member of the thread * If we are the last non-leader member of the thread
* group, and the leader is zombie, then notify the * group, and the leader is zombie, then notify the
* group leader's parent process. * group leader's parent process. (if it wants notification.)
*/ */
if (p->group_leader != p && thread_group_empty(p)) leader = p->group_leader;
do_notify_parent(p->group_leader, p->group_leader->exit_signal); if (leader != p && thread_group_empty(leader) &&
leader->state == TASK_ZOMBIE && leader->exit_signal != -1)
do_notify_parent(leader, leader->exit_signal);
p->parent->cutime += p->utime + p->cutime; p->parent->cutime += p->utime + p->cutime;
p->parent->cstime += p->stime + p->cstime; p->parent->cstime += p->stime + p->cstime;
...@@ -159,7 +160,7 @@ static int __will_become_orphaned_pgrp(int pgrp, task_t *ignored_task) ...@@ -159,7 +160,7 @@ static int __will_become_orphaned_pgrp(int pgrp, task_t *ignored_task)
for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) {
if (p == ignored_task if (p == ignored_task
|| p->state == TASK_ZOMBIE || p->state >= TASK_ZOMBIE
|| p->real_parent->pid == 1) || p->real_parent->pid == 1)
continue; continue;
if (p->real_parent->pgrp != pgrp if (p->real_parent->pgrp != pgrp
...@@ -435,8 +436,11 @@ void exit_mm(struct task_struct *tsk) ...@@ -435,8 +436,11 @@ void exit_mm(struct task_struct *tsk)
static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper) static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper)
{ {
/* Make sure we're not reparenting to ourselves. */ /*
if (p == reaper) * Make sure we're not reparenting to ourselves and that
* the parent is not a zombie.
*/
if (p == reaper || reaper->state >= TASK_ZOMBIE)
p->real_parent = child_reaper; p->real_parent = child_reaper;
else else
p->real_parent = reaper; p->real_parent = reaper;
...@@ -774,9 +778,10 @@ static int eligible_child(pid_t pid, int options, task_t *p) ...@@ -774,9 +778,10 @@ static int eligible_child(pid_t pid, int options, task_t *p)
asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru) asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru)
{ {
int flag, retval;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
struct task_struct *tsk; struct task_struct *tsk;
unsigned long state;
int flag, retval;
if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
return -EINVAL; return -EINVAL;
...@@ -827,7 +832,15 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -827,7 +832,15 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
*/ */
if (ret == 2) if (ret == 2)
continue; 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); read_unlock(&tasklist_lock);
retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
if (!retval && stat_addr) { if (!retval && stat_addr) {
if (p->sig->group_exit) if (p->sig->group_exit)
...@@ -835,13 +848,16 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc ...@@ -835,13 +848,16 @@ asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struc
else else
retval = put_user(p->exit_code, stat_addr); retval = put_user(p->exit_code, stat_addr);
} }
if (retval) if (retval) {
p->state = TASK_ZOMBIE;
goto end_wait4; goto end_wait4;
}
retval = p->pid; retval = p->pid;
if (p->real_parent != p->parent) { if (p->real_parent != p->parent) {
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
__ptrace_unlink(p); __ptrace_unlink(p);
do_notify_parent(p, SIGCHLD); do_notify_parent(p, SIGCHLD);
p->state = TASK_ZOMBIE;
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
} else } else
release_task(p); release_task(p);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment