I missed that ptrace_reuse_engine()->utrace_barrier() can return ERESTARTSYS if signal_pending(). Unfortunately, it is too late to abort and restart this syscall, we already changed ctx->resume.
Introduce utrace_barrier_uninterruptible() which calls utrace_barrier() and ignores ERESTARTSYS. Note that we do schedule_timeout_uninterruptible() to avoid the livelock. utrace_barrier()->schedule_timeout_interruptible() is nop in this case. This helper will have another user, exit_ptrace()->ptrace_detach_task(), it obviously can't restart too. Also, improve (and fix) the debugging engine->ops checks. In theory the tracee can exit right after utrace_barrier() succeeds, this can lead to the false warning. Signed-off-by: Oleg Nesterov <o...@redhat.com> --- kernel/ptrace-utrace.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) --- kstub/kernel/ptrace-utrace.c~1_fix_reuse_barrier 2010-09-20 03:43:28.000000000 +0200 +++ kstub/kernel/ptrace-utrace.c 2010-09-20 03:53:30.000000000 +0200 @@ -103,6 +103,19 @@ static struct utrace_engine *ptrace_look &ptrace_utrace_ops, NULL); } +static int utrace_barrier_uninterruptible(struct task_struct *target, + struct utrace_engine *engine) +{ + for (;;) { + int err = utrace_barrier(target, engine); + + if (err != -ERESTARTSYS) + return err; + + schedule_timeout_uninterruptible(1); + } +} + static struct utrace_engine * ptrace_reuse_engine(struct task_struct *tracee) { @@ -129,12 +142,16 @@ ptrace_reuse_engine(struct task_struct * if (!err || err == -EINPROGRESS) { ctx->resume = UTRACE_RESUME; /* synchronize with ptrace_report_signal() */ - err = utrace_barrier(tracee, engine); + err = utrace_barrier_uninterruptible(tracee, engine); } - WARN_ON(!err != (engine->ops == &ptrace_utrace_ops)); - if (!err) + if (!err) { + WARN_ON(engine->ops != &ptrace_utrace_ops && + !tracee->exit_state); return engine; + } + + WARN_ON(engine->ops == &ptrace_utrace_ops); } utrace_engine_put(engine);