Add the new helper, ptrace_signal_wake_up(), change ptrace.c/signal.c
to use it instead of signal_wake_up() to wake up a STOPPED/TRACED task.
The new helper does almost the same, except:
- it doesn't use the TASK_WAKEKILL bit to wake up the TRACED
or STOPPED task, it uses __TASK_STOPPED | __TASK_TRACED
explicitly. This is what ptrace actually wants, it should
never wake up a TASK_KILLABLE task.
This should be cleanuped upatream, signal_wake_up() should
take the state as an argument, not a boolean. Until then
we add a new static helper.
- it uses wake_up_quiescent() instead of wake_up_state().
Thereafter every change from STOPPED/TRACED to RUNNING is done via
wake_up_quiescent().
Signed-off-by: Oleg Nesterov <oleg(a)redhat.com>
---
include/linux/ptrace.h | 1 +
kernel/ptrace.c | 20 ++++++++++++++++----
kernel/signal.c | 2 +-
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 800f113..6d9282a 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -113,6 +113,7 @@
#include <linux/compiler.h> /* For unlikely. */
#include <linux/sched.h> /* For struct task_struct. */
+extern void ptrace_signal_wake_up(struct task_struct *p, int quiescent);
extern long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4194664..1a50090 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -25,6 +25,18 @@
#include <linux/hw_breakpoint.h>
#include <linux/cn_proc.h>
+void ptrace_signal_wake_up(struct task_struct *p, int quiescent)
+{
+ unsigned int state;
+
+ set_tsk_thread_flag(p, TIF_SIGPENDING);
+
+ state = TASK_INTERRUPTIBLE;
+ if (quiescent)
+ state |= (__TASK_STOPPED | __TASK_TRACED);
+ if (!wake_up_quiescent(p, state))
+ kick_process(p);
+}
static int ptrace_trapping_sleep_fn(void *flags)
{
@@ -106,7 +118,7 @@ void __ptrace_unlink(struct task_struct *child)
* TASK_KILLABLE sleeps.
*/
if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child))
- signal_wake_up(child, task_is_traced(child));
+ ptrace_signal_wake_up(child, task_is_traced(child));
spin_unlock(&child->sighand->siglock);
}
@@ -296,7 +308,7 @@ static int ptrace_attach(struct task_struct *task, long request,
*/
if (task_is_stopped(task) &&
task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
- signal_wake_up(task, 1);
+ ptrace_signal_wake_up(task, 1);
spin_unlock(&task->sighand->siglock);
@@ -731,7 +743,7 @@ int ptrace_request(struct task_struct *child, long request,
* tracee into STOP.
*/
if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
- signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
+ ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
unlock_task_sighand(child, &flags);
ret = 0;
@@ -760,7 +772,7 @@ int ptrace_request(struct task_struct *child, long request,
* of this trap and now. Trigger re-trap immediately.
*/
if (child->jobctl & JOBCTL_TRAP_NOTIFY)
- signal_wake_up(child, true);
+ ptrace_signal_wake_up(child, true);
unlock_task_sighand(child, &flags);
ret = 0;
diff --git a/kernel/signal.c b/kernel/signal.c
index 3e8e0b1..0dc6abb 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -854,7 +854,7 @@ static void ptrace_trap_notify(struct task_struct *t)
assert_spin_locked(&t->sighand->siglock);
task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY);
- signal_wake_up(t, t->jobctl & JOBCTL_LISTENING);
+ ptrace_signal_wake_up(t, t->jobctl & JOBCTL_LISTENING);
}
/*
--
1.5.5.1