| From d13edb33989cb84e8331cdc1aea69521870d5312 Mon Sep 17 00:00:00 2001 |
| From: "bsegall@google.com" <bsegall@google.com> |
| Date: Fri, 7 Apr 2017 16:04:51 -0700 |
| Subject: [PATCH] ptrace: fix PTRACE_LISTEN race corrupting task->state |
| |
| commit 5402e97af667e35e54177af8f6575518bf251d51 upstream. |
| |
| In PT_SEIZED + LISTEN mode STOP/CONT signals cause a wakeup against |
| __TASK_TRACED. If this races with the ptrace_unfreeze_traced at the end |
| of a PTRACE_LISTEN, this can wake the task /after/ the check against |
| __TASK_TRACED, but before the reset of state to TASK_TRACED. This |
| causes it to instead clobber TASK_WAKING, allowing a subsequent wakeup |
| against TRACED while the task is still on the rq wake_list, corrupting |
| it. |
| |
| Oleg said: |
| "The kernel can crash or this can lead to other hard-to-debug problems. |
| In short, "task->state = TASK_TRACED" in ptrace_unfreeze_traced() |
| assumes that nobody else can wake it up, but PTRACE_LISTEN breaks the |
| contract. Obviusly it is very wrong to manipulate task->state if this |
| task is already running, or WAKING, or it sleeps again" |
| |
| [akpm@linux-foundation.org: coding-style fixes] |
| Fixes: 9899d11f ("ptrace: ensure arch_ptrace/ptrace_request can never race with SIGKILL") |
| Link: http://lkml.kernel.org/r/xm26y3vfhmkp.fsf_-_@bsegall-linux.mtv.corp.google.com |
| Signed-off-by: Ben Segall <bsegall@google.com> |
| Acked-by: Oleg Nesterov <oleg@redhat.com> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/kernel/ptrace.c b/kernel/ptrace.c |
| index 7b20baea41e7..4baf6d6a6d8c 100644 |
| --- a/kernel/ptrace.c |
| +++ b/kernel/ptrace.c |
| @@ -150,11 +150,17 @@ static void ptrace_unfreeze_traced(struct task_struct *task) |
| |
| WARN_ON(!task->ptrace || task->parent != current); |
| |
| + /* |
| + * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely. |
| + * Recheck state under the lock to close this race. |
| + */ |
| spin_lock_irq(&task->sighand->siglock); |
| - if (__fatal_signal_pending(task)) |
| - wake_up_state(task, __TASK_TRACED); |
| - else |
| - task->state = TASK_TRACED; |
| + if (task->state == __TASK_TRACED) { |
| + if (__fatal_signal_pending(task)) |
| + wake_up_state(task, __TASK_TRACED); |
| + else |
| + task->state = TASK_TRACED; |
| + } |
| spin_unlock_irq(&task->sighand->siglock); |
| } |
| |
| -- |
| 2.12.0 |
| |