]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] execve TGID dethreading bug fix
authorDavid Howells <dhowells@redhat.com>
Thu, 7 Mar 2002 08:46:44 +0000 (00:46 -0800)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Thu, 7 Mar 2002 08:46:44 +0000 (00:46 -0800)
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).

fs/exec.c
include/asm-i386/siginfo.h

index 9403cb1c57b68b99ecc4d65d490ee404a8166fa6..82b56d3233ea50b3d1cf59667c79e59766963c8e 100644 (file)
--- 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(&current->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))
index d2686eda126d89003de7679fa997d5b47cc7d661..0f7c3c86dc1eeea24b522c1c1e881db64358cad5 100644 (file)
@@ -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)