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);

Reply via email to