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@xxxxxxxxxx> --- 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 _______________________________________________ kernel mailing list kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel