summaryrefslogtreecommitdiff
path: root/kernel/ptrace.c
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2013-08-29 16:21:04 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-10 00:18:37 (GMT)
commit9141a662b864ace2e16c3530279e24291b4ed788 (patch)
tree86839508ca976e53a25a550e570ad48f03fe92cc /kernel/ptrace.c
parent70b99bd28bdb7efba6f927adad7cc03c52161c94 (diff)
downloadlinux-fsl-qoriq-9141a662b864ace2e16c3530279e24291b4ed788.tar.xz
ptrace: fix ptrace vs tasklist_lock race
As explained by Alexander Fyodorov <halcy@yandex.ru>: |read_lock(&tasklist_lock) in ptrace_stop() is converted to mutex on RT kernel, |and it can remove __TASK_TRACED from task->state (by moving it to |task->saved_state). If parent does wait() on child followed by a sys_ptrace |call, the following race can happen: | |- child sets __TASK_TRACED in ptrace_stop() |- parent does wait() which eventually calls wait_task_stopped() and returns | child's pid |- child blocks on read_lock(&tasklist_lock) in ptrace_stop() and moves | __TASK_TRACED flag to saved_state |- parent calls sys_ptrace, which calls ptrace_check_attach() and wait_task_inactive() The patch is based on his initial patch where an additional check is added in case the __TASK_TRACED moved to ->saved_state. The pi_lock is taken in case the caller is interrupted between looking into ->state and ->saved_state. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r--kernel/ptrace.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 1f4bcb3..fddaf65 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -135,7 +135,12 @@ static bool ptrace_freeze_traced(struct task_struct *task)
spin_lock_irq(&task->sighand->siglock);
if (task_is_traced(task) && !__fatal_signal_pending(task)) {
- task->state = __TASK_TRACED;
+ raw_spin_lock_irq(&task->pi_lock);
+ if (task->state & __TASK_TRACED)
+ task->state = __TASK_TRACED;
+ else
+ task->saved_state = __TASK_TRACED;
+ raw_spin_unlock_irq(&task->pi_lock);
ret = true;
}
spin_unlock_irq(&task->sighand->siglock);