diff options
| author | David Howells <dhowells@redhat.com> | 2002-03-07 00:46:44 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-03-07 00:46:44 -0800 |
| commit | a55b91121e897472cf11c2caab49013eb013965e (patch) | |
| tree | 8b3445589219c879a2457d380e6fb9548119b64f | |
| parent | 8656699fa8ec57e222af910749978cfcfb1cd821 (diff) | |
[PATCH] execve TGID dethreading bug fix
kill all subsidiary threads in a thread group when the main thread
exits.
Features:
- It sends the subsidiary threads SIGKILL with SI_DETHREAD.
- Subsidiary threads doing an execve() just leave the thread group (rather
than forcing the master thread to do an execve() which would be more POSIX
like).
| -rw-r--r-- | fs/exec.c | 53 | ||||
| -rw-r--r-- | include/asm-i386/siginfo.h | 1 |
2 files changed, 41 insertions, 13 deletions
diff --git a/fs/exec.c b/fs/exec.c index 9403cb1c57b6..82b56d3233ea 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -509,23 +509,49 @@ static inline void flush_old_files(struct files_struct * files) /* * An execve() will automatically "de-thread" the process. - * Note: we don't have to hold the tasklist_lock to test - * whether we migth need to do this. If we're not part of - * a thread group, there is no way we can become one - * dynamically. And if we are, we only need to protect the - * unlink - even if we race with the last other thread exit, - * at worst the list_del_init() might end up being a no-op. + * - if a master thread (PID==TGID) is doing this, then all subsidiary threads + * will be killed (otherwise there will end up being two independent thread + * groups with the same TGID). + * - if a subsidary thread is doing this, then it just leaves the thread group */ -static inline void de_thread(struct task_struct *tsk) +static void de_thread(struct task_struct *tsk) { - if (!list_empty(&tsk->thread_group)) { - write_lock_irq(&tasklist_lock); + struct task_struct *sub; + struct list_head *head, *ptr; + struct siginfo info; + int pause; + + write_lock_irq(&tasklist_lock); + + if (tsk->tgid != tsk->pid) { + /* subsidiary thread - just escapes the group */ + list_del_init(&tsk->thread_group); + tsk->tgid = tsk->pid; + pause = 0; + } + else { + /* master thread - kill all subsidiary threads */ + info.si_signo = SIGKILL; + info.si_errno = 0; + info.si_code = SI_DETHREAD; + info.si_pid = current->pid; + info.si_uid = current->uid; + + head = tsk->thread_group.next; list_del_init(&tsk->thread_group); - write_unlock_irq(&tasklist_lock); + + list_for_each(ptr,head) { + sub = list_entry(ptr,struct task_struct,thread_group); + send_sig_info(SIGKILL,&info,sub); + } + + pause = 1; } - /* Minor oddity: this might stay the same. */ - tsk->tgid = tsk->pid; + write_unlock_irq(&tasklist_lock); + + /* give the subsidiary threads a chance to clean themselves up */ + if (pause) yield(); } int flush_old_exec(struct linux_binprm * bprm) @@ -566,7 +592,8 @@ int flush_old_exec(struct linux_binprm * bprm) flush_thread(); - de_thread(current); + if (!list_empty(¤t->thread_group)) + de_thread(current); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || permission(bprm->file->f_dentry->d_inode,MAY_READ)) diff --git a/include/asm-i386/siginfo.h b/include/asm-i386/siginfo.h index d2686eda126d..0f7c3c86dc1e 100644 --- a/include/asm-i386/siginfo.h +++ b/include/asm-i386/siginfo.h @@ -108,6 +108,7 @@ typedef struct siginfo { #define SI_ASYNCIO -4 /* sent by AIO completion */ #define SI_SIGIO -5 /* sent by queued SIGIO */ #define SI_TKILL -6 /* sent by tkill system call */ +#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ #define SI_FROMUSER(siptr) ((siptr)->si_code <= 0) #define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) |
