When taking the task_list_lock in de_thread also take the siglock.  This
makes de_thread closer to fork the canonical place where these locks are
taken.

To complete the defensiveness always take siglock when clearing
group_exit_task and notify_count.

This gives now gives the guarantee that group_exit_task and notify_count
are now always changed under siglock.  As anything multi-threaded in exec
is a rare and slow path I don't think we care if we take an extra lock in
practice.

The practical reason for doing this is to enable setting signal->flags along
with group_exit_task so that the function signal_group_exit can be simplified.

Signed-off-by: "Eric W. Biederman" <[email protected]>
---
 fs/exec.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index e6e8a9a70327..33b5d9229c01 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1171,6 +1171,7 @@ static int de_thread(struct task_struct *tsk)
                for (;;) {
                        cgroup_threadgroup_change_begin(tsk);
                        write_lock_irq(&tasklist_lock);
+                       spin_lock(lock);
                        /*
                         * Do this under tasklist_lock to ensure that
                         * exit_notify() can't miss ->group_exit_task
@@ -1179,6 +1180,7 @@ static int de_thread(struct task_struct *tsk)
                        if (likely(leader->exit_state))
                                break;
                        __set_current_state(TASK_KILLABLE);
+                       spin_unlock(lock);
                        write_unlock_irq(&tasklist_lock);
                        cgroup_threadgroup_change_end(tsk);
                        schedule();
@@ -1234,14 +1236,17 @@ static int de_thread(struct task_struct *tsk)
                 */
                if (unlikely(leader->ptrace))
                        __wake_up_parent(leader, leader->parent);
+               spin_unlock(lock);
                write_unlock_irq(&tasklist_lock);
                cgroup_threadgroup_change_end(tsk);
 
                release_task(leader);
        }
 
+       spin_lock_irq(lock);
        sig->group_exit_task = NULL;
        sig->notify_count = 0;
+       spin_unlock_irq(lock);
 
 no_thread_group:
        /* we have changed execution domain */
@@ -1252,10 +1257,12 @@ static int de_thread(struct task_struct *tsk)
 
 killed:
        /* protects against exit_notify() and __exit_signal() */
-       read_lock(&tasklist_lock);
+       read_lock_irq(&tasklist_lock);
+       spin_lock(lock);
        sig->group_exit_task = NULL;
        sig->notify_count = 0;
-       read_unlock(&tasklist_lock);
+       spin_unlock(lock);
+       read_unlock_irq(&tasklist_lock);
        return -EAGAIN;
 }
 
-- 
2.20.1

Reply via email to