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 oleg@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 9de3ecf..56b8fc1 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -589,6 +589,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;
@@ -617,7 +619,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 0f9af0b..71f5cca 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1878,7 +1878,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(¤t->sighand->siglock); __set_current_state(TASK_RUNNING); + spin_unlock_irq(¤t->sighand->siglock); + if (clear_code) current->exit_code = 0; read_unlock(&tasklist_lock);