Introduce ptrace_attach_task() and ptrace_abort_attach(). There are
simplified versions of prepare_ptrace_attach/finish_ptrace_attach and
should be used instead. Change ptrace_attach() accordingly.

This patch doesn't kill the old helpers and doesn't change ptrace_traceme()
because I noticed it has another bug.

Note the "XXX" comment in ptrace_abort_attach(). I am not sure a simple
UTRACE_DETACH is always correct. We already did utrace_set_events(), we
must not, say, lose a signal if it comes in between.

---

 kernel/ptrace.c |   57 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 49 insertions(+), 8 deletions(-)

--- PU/kernel/ptrace.c~07_ATTACH        2009-08-13 18:41:46.000000000 +0200
+++ PU/kernel/ptrace.c  2009-08-17 15:31:07.000000000 +0200
@@ -509,6 +509,46 @@ static struct utrace_engine *prepare_ptr
        return engine;
 }
 
+/*
+ * Attach a utrace engine for ptrace and set up its event mask.
+ * Returns error code or 0 on success.
+ */
+static int ptrace_attach_task(struct task_struct *tracee)
+{
+       struct utrace_engine *engine;
+       unsigned long events;
+
+       engine = utrace_attach_task(tracee, UTRACE_ATTACH_CREATE |
+                                               UTRACE_ATTACH_EXCLUSIVE |
+                                               UTRACE_ATTACH_MATCH_OPS,
+                                               &ptrace_utrace_ops, NULL);
+       if (unlikely(IS_ERR(engine))) {
+               int err = PTR_ERR(engine);
+               if (err != -ESRCH && err != -ERESTARTNOINTR)
+                       err = -EPERM ;
+               return err;
+       }
+       /*
+        * We need QUIESCE for resume handling, CLONE to check
+        * for CLONE_PTRACE, other events are always reported.
+        */
+       events = UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
+                UTRACE_EVENT(EXEC) | UTRACE_EVENT_SIGNAL_ALL;
+       /*
+        * It can fail only if the tracee is dead, the caller
+        * must notice this before setting PT_PTRACED.
+        */
+       utrace_set_events(tracee, engine, events);
+       utrace_engine_put(engine);
+       return 0;
+}
+
+static void ptrace_abort_attach(struct task_struct *tracee)
+{
+       /* XXX we raced with detach. Double check UTRACE_DETACH is enough */
+       ptrace_detach_task(tracee, 0);
+}
+
 int ptrace_check_attach(struct task_struct *child, int kill)
 {
        struct utrace_engine *engine;
@@ -587,19 +627,14 @@ bool ptrace_may_access(struct task_struc
 int ptrace_attach(struct task_struct *task)
 {
        int retval;
-       struct utrace_engine *engine;
 
        audit_ptrace(task);
 
        retval = -EPERM;
        if (unlikely(task->flags & PF_KTHREAD))
-               return retval;
+               goto out;
        if (same_thread_group(task, current))
-               return retval;
-
-       engine = prepare_ptrace_attach(task, current);
-       if (unlikely(IS_ERR(engine)))
-               return PTR_ERR(engine);
+               goto out;
 
        /*
         * Protect exec's credential calculations against our interference;
@@ -616,6 +651,10 @@ int ptrace_attach(struct task_struct *ta
        if (retval)
                goto unlock_creds;
 
+       retval = ptrace_attach_task(task);
+       if (unlikely(retval))
+               goto unlock_creds;
+
        write_lock_irq(&tasklist_lock);
        retval = -EPERM;
        if (unlikely(task->exit_state))
@@ -633,10 +672,12 @@ int ptrace_attach(struct task_struct *ta
        retval = 0;
 unlock_tasklist:
        write_unlock_irq(&tasklist_lock);
+       if (retval)
+               ptrace_abort_attach(task);
 unlock_creds:
        mutex_unlock(&task->cred_guard_mutex);
 out:
-       return finish_ptrace_attach(task, engine, retval);
+       return retval;
 }
 
 /**

Reply via email to