Commit a55b9112 authored by David Howells's avatar David Howells Committed by Linus Torvalds

[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).
parent 8656699f
...@@ -509,23 +509,49 @@ static inline void flush_old_files(struct files_struct * files) ...@@ -509,23 +509,49 @@ static inline void flush_old_files(struct files_struct * files)
/* /*
* An execve() will automatically "de-thread" the process. * An execve() will automatically "de-thread" the process.
* Note: we don't have to hold the tasklist_lock to test * - if a master thread (PID==TGID) is doing this, then all subsidiary threads
* whether we migth need to do this. If we're not part of * will be killed (otherwise there will end up being two independent thread
* a thread group, there is no way we can become one * groups with the same TGID).
* dynamically. And if we are, we only need to protect the * - if a subsidary thread is doing this, then it just leaves the thread group
* unlink - even if we race with the last other thread exit,
* at worst the list_del_init() might end up being a no-op.
*/ */
static inline void de_thread(struct task_struct *tsk) static void de_thread(struct task_struct *tsk)
{ {
if (!list_empty(&tsk->thread_group)) { struct task_struct *sub;
struct list_head *head, *ptr;
struct siginfo info;
int pause;
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
if (tsk->tgid != tsk->pid) {
/* subsidiary thread - just escapes the group */
list_del_init(&tsk->thread_group); list_del_init(&tsk->thread_group);
write_unlock_irq(&tasklist_lock); 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;
/* Minor oddity: this might stay the same. */ head = tsk->thread_group.next;
tsk->tgid = tsk->pid; list_del_init(&tsk->thread_group);
list_for_each(ptr,head) {
sub = list_entry(ptr,struct task_struct,thread_group);
send_sig_info(SIGKILL,&info,sub);
}
pause = 1;
}
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) int flush_old_exec(struct linux_binprm * bprm)
...@@ -566,6 +592,7 @@ int flush_old_exec(struct linux_binprm * bprm) ...@@ -566,6 +592,7 @@ int flush_old_exec(struct linux_binprm * bprm)
flush_thread(); flush_thread();
if (!list_empty(&current->thread_group))
de_thread(current); de_thread(current);
if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
......
...@@ -108,6 +108,7 @@ typedef struct siginfo { ...@@ -108,6 +108,7 @@ typedef struct siginfo {
#define SI_ASYNCIO -4 /* sent by AIO completion */ #define SI_ASYNCIO -4 /* sent by AIO completion */
#define SI_SIGIO -5 /* sent by queued SIGIO */ #define SI_SIGIO -5 /* sent by queued SIGIO */
#define SI_TKILL -6 /* sent by tkill system call */ #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_FROMUSER(siptr) ((siptr)->si_code <= 0)
#define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0) #define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0)
......
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