change ptrace_resume() and ptrace_stop() to take ->siglock around changing
task->state from TRACED to RUNNING.

With this patch __TASK_TRACED/STOPPED bits are fully protected by ->siglock,
nobody can set or clear these bits without ->siglock held.

Signed-off-by: Oleg Nesterov <o...@redhat.com>
---
 kernel/ptrace.c |    8 +++++++-
 kernel/signal.c |    3 +++
 2 files changed, 10 insertions(+), 1 deletions(-)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2df1157..9988b13 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -534,6 +534,8 @@ static int ptrace_setsiginfo(struct task_struct *child, 
const siginfo_t *info)
 static int ptrace_resume(struct task_struct *child, long request,
                         unsigned long data)
 {
+       unsigned long flags;
+
        if (!valid_signal(data))
                return -EIO;
 
@@ -562,7 +564,11 @@ static int ptrace_resume(struct task_struct *child, long 
request,
        }
 
        child->exit_code = data;
-       wake_up_state(child, __TASK_TRACED);
+
+       if (lock_task_sighand(child, &flags)) {
+               wake_up_state(child, __TASK_TRACED);
+               unlock_task_sighand(child, &flags);
+       }
 
        return 0;
 }
diff --git a/kernel/signal.c b/kernel/signal.c
index ff76786..2138cee 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1799,7 +1799,10 @@ static void ptrace_stop(int exit_code, int why, int 
clear_code, siginfo_t *info)
                if (gstop_done)
                        do_notify_parent_cldstop(current, false, why);
 
+               spin_lock_irq(&current->sighand->siglock);
                __set_current_state(TASK_RUNNING);
+               spin_unlock_irq(&current->sighand->siglock);
+
                if (clear_code)
                        current->exit_code = 0;
                read_unlock(&tasklist_lock);
-- 
1.5.5.1


Reply via email to