Skip to content

Commit 926e1ed

Browse files
bsegall@google.comgregkh
authored andcommitted
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: 9899d11 ("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> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 5a69c2b commit 926e1ed

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

kernel/ptrace.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,17 @@ static void ptrace_unfreeze_traced(struct task_struct *task)
151151

152152
WARN_ON(!task->ptrace || task->parent != current);
153153

154+
/*
155+
* PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely.
156+
* Recheck state under the lock to close this race.
157+
*/
154158
spin_lock_irq(&task->sighand->siglock);
155-
if (__fatal_signal_pending(task))
156-
wake_up_state(task, __TASK_TRACED);
157-
else
158-
task->state = TASK_TRACED;
159+
if (task->state == __TASK_TRACED) {
160+
if (__fatal_signal_pending(task))
161+
wake_up_state(task, __TASK_TRACED);
162+
else
163+
task->state = TASK_TRACED;
164+
}
159165
spin_unlock_irq(&task->sighand->siglock);
160166
}
161167

0 commit comments

Comments
 (0)