mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 04:40:51 +07:00
ptrace: fix PTRACE_LISTEN race corrupting task->state
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>
This commit is contained in:
parent
d79bf21e0e
commit
5402e97af6
@ -184,11 +184,17 @@ static void ptrace_unfreeze_traced(struct task_struct *task)
|
|||||||
|
|
||||||
WARN_ON(!task->ptrace || task->parent != current);
|
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);
|
spin_lock_irq(&task->sighand->siglock);
|
||||||
if (__fatal_signal_pending(task))
|
if (task->state == __TASK_TRACED) {
|
||||||
wake_up_state(task, __TASK_TRACED);
|
if (__fatal_signal_pending(task))
|
||||||
else
|
wake_up_state(task, __TASK_TRACED);
|
||||||
task->state = TASK_TRACED;
|
else
|
||||||
|
task->state = TASK_TRACED;
|
||||||
|
}
|
||||||
spin_unlock_irq(&task->sighand->siglock);
|
spin_unlock_irq(&task->sighand->siglock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user