diff options
| author | Ingo Molnar <mingo@elte.hu> | 2002-09-15 19:28:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-09-15 19:28:22 -0700 |
| commit | 25f0da24a7638cdf4890f7fcc03ce9c2b6644145 (patch) | |
| tree | 981e7f7061a65c06a96f57ae5753e0e8d89e6e0a /fs/exec.c | |
| parent | 3568bea5afd07a98f146d0f4bd40d6cc36a210c5 (diff) | |
[PATCH] thread-exec-fix-2.5.35-A5, BK-curr
This fixes a number of sys_execve() problems:
- ptrace of thread groups over exec works again.
- if the exec() is done in a non-leader thread then we must inherit the
parent links properly - otherwise the shell will see an early
child-exit notification.
- if the exec()-ing thread is detached then make it use SIGCHLD like the
leader thread.
- wait for the leader thread to become TASK_ZOMBIE properly -
wait_task_inactive() alone was not enough. This should be a rare
codepath.
now sys_execve() from thread groups works as expected in every combination
i could test: standalone, from the leader thread, from one of the child
threads, ptraced, non-ptraced, SMP and UP.
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/fs/exec.c b/fs/exec.c index 585b3763b6f8..ec95e796ec22 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -41,6 +41,7 @@ #include <linux/module.h> #include <linux/namei.h> #include <linux/proc_fs.h> +#include <linux/ptrace.h> #include <asm/uaccess.h> #include <asm/pgalloc.h> @@ -577,11 +578,17 @@ static inline int de_thread(struct signal_struct *oldsig) * and to assume its PID: */ if (current->pid != current->tgid) { - struct task_struct *leader = current->group_leader; + struct task_struct *leader = current->group_leader, *parent; struct dentry *proc_dentry1, *proc_dentry2; - unsigned long state; + unsigned long state, ptrace; - wait_task_inactive(leader); + /* + * Wait for the thread group leader to be a zombie. + * It should already be zombie at this point, most + * of the time. + */ + while (leader->state != TASK_ZOMBIE) + yield(); write_lock_irq(&tasklist_lock); proc_dentry1 = clean_proc_dentry(current); @@ -597,10 +604,33 @@ static inline int de_thread(struct signal_struct *oldsig) * two threads with a switched PID, and release * the former thread group leader: */ + ptrace = leader->ptrace; + parent = leader->parent; + + ptrace_unlink(leader); + ptrace_unlink(current); unhash_pid(current); unhash_pid(leader); + remove_parent(current); + remove_parent(leader); + /* + * Split up the last two remaining members of the + * thread group: + */ + list_del_init(&leader->thread_group); + leader->pid = leader->tgid = current->pid; current->pid = current->tgid; + current->parent = current->real_parent = leader->real_parent; + leader->parent = leader->real_parent = child_reaper; + current->exit_signal = SIGCHLD; + + add_parent(current, current->parent); + add_parent(leader, leader->parent); + if (ptrace) { + current->ptrace = ptrace; + __ptrace_link(current, parent); + } hash_pid(current); hash_pid(leader); @@ -608,8 +638,9 @@ static inline int de_thread(struct signal_struct *oldsig) state = leader->state; write_unlock_irq(&tasklist_lock); - if (state == TASK_ZOMBIE) - release_task(leader); + if (state != TASK_ZOMBIE) + BUG(); + release_task(leader); put_proc_dentry(proc_dentry1); put_proc_dentry(proc_dentry2); |
