Introduce the empty ptrace_utrace_ops, change attach/detach to add/remove
utrace_engine whis this ops.

For this series it will be empty, no methods and we never call set_events().
We use it only to cooperate with other engines when it comes to stop/wakeup.

---

 kernel/ptrace.c |  147 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 95 insertions(+), 52 deletions(-)

--- MINI/kernel/ptrace.c~2_PTRACE_ENGINE        2009-07-13 17:44:27.000000000 
+0200
+++ MINI/kernel/ptrace.c        2009-08-25 17:00:35.000000000 +0200
@@ -24,6 +24,43 @@
 #include <linux/syscalls.h>
 #include <linux/uaccess.h>
 
+static const struct utrace_engine_ops ptrace_utrace_ops; /* empty */
+
+static void ptrace_detach_task(struct task_struct *child)
+{
+       struct utrace_engine *engine;
+       int ret;
+
+       engine = utrace_attach_task(child, UTRACE_ATTACH_MATCH_OPS,
+                                   &ptrace_utrace_ops, NULL);
+       if (unlikely(IS_ERR(engine)))
+               return;
+
+       ret = utrace_control(child, engine, UTRACE_DETACH);
+       WARN_ON(ret && ret != -EINPROGRESS &&
+               ret != -ESRCH && ret != -EALREADY);
+
+       utrace_engine_put(engine);
+}
+
+static int ptrace_attach_task(struct task_struct *tracee)
+{
+       struct utrace_engine *engine;
+
+       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;
+       }
+
+       utrace_engine_put(engine);
+       return 0;
+}
 
 /*
  * ptrace a task: make the debugger its new parent and
@@ -196,19 +233,20 @@ int ptrace_attach(struct task_struct *ta
 
        task_lock(task);
        retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
-       if (!retval && exclude_ptrace(task))
-               retval = -EBUSY;
        task_unlock(task);
        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))
                goto unlock_tasklist;
-       if (task->ptrace)
-               goto unlock_tasklist;
 
+       BUG_ON(task->ptrace);
        task->ptrace = PT_PTRACED;
        if (capable(CAP_SYS_PTRACE))
                task->ptrace |= PT_PTRACE_CAP;
@@ -233,27 +271,30 @@ out:
  */
 int ptrace_traceme(void)
 {
-       int ret = -EPERM;
+       bool detach = true;
+       int ret = ptrace_attach_task(current);
 
-       if (exclude_ptrace(current)) /* XXX locking */
-               return -EBUSY;
+       if (unlikely(ret))
+               return ret;
 
+       ret = -EPERM;
        write_lock_irq(&tasklist_lock);
-       /* Are we already being traced? */
-       if (!current->ptrace) {
-               ret = security_ptrace_traceme(current->parent);
-               /*
-                * Check PF_EXITING to ensure ->real_parent has not passed
-                * exit_ptrace(). Otherwise we don't report the error but
-                * pretend ->real_parent untraces us right after return.
-                */
-               if (!ret && !(current->real_parent->flags & PF_EXITING)) {
-                       current->ptrace = PT_PTRACED;
-                       __ptrace_link(current, current->real_parent);
-               }
+       BUG_ON(current->ptrace);
+       ret = security_ptrace_traceme(current->parent);
+       /*
+        * Check PF_EXITING to ensure ->real_parent has not passed
+        * exit_ptrace(). Otherwise we don't report the error but
+        * pretend ->real_parent untraces us right after return.
+        */
+       if (!ret && !(current->real_parent->flags & PF_EXITING)) {
+               current->ptrace = PT_PTRACED;
+               __ptrace_link(current, current->real_parent);
+               detach = false;
        }
        write_unlock_irq(&tasklist_lock);
 
+       if (detach)
+               ptrace_detach_task(current);
        return ret;
 }
 
@@ -305,56 +346,58 @@ static bool __ptrace_detach(struct task_
        return false;
 }
 
-int ptrace_detach(struct task_struct *child, unsigned int data)
+static void ptrace_do_detach(struct task_struct *tracee, unsigned int data)
 {
-       bool dead = false;
-
-       if (!valid_signal(data))
-               return -EIO;
-
-       /* Architecture-specific hardware disable .. */
-       ptrace_disable(child);
-       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+       bool detach, release;
 
        write_lock_irq(&tasklist_lock);
        /*
-        * This child can be already killed. Make sure de_thread() or
+        * This tracee can be already killed. Make sure de_thread() or
         * our sub-thread doing do_wait() didn't do release_task() yet.
         */
-       if (child->ptrace) {
-               child->exit_code = data;
-               dead = __ptrace_detach(current, child);
-               if (!child->exit_state)
-                       wake_up_process(child);
+       detach = (tracee->ptrace != 0);
+       if (likely(detach)) {
+               if (valid_signal(data))
+                       tracee->exit_code = data;
+               release = __ptrace_detach(current, tracee);
        }
        write_unlock_irq(&tasklist_lock);
 
-       if (unlikely(dead))
-               release_task(child);
+       if (likely(detach)) {
+               if (unlikely(release))
+                       release_task(tracee);
+               else
+                       ptrace_detach_task(tracee);
+       }
+}
+
+int ptrace_detach(struct task_struct *child, unsigned int data)
+{
+       if (!valid_signal(data))
+               return -EIO;
+
+       ptrace_do_detach(child, data);
 
        return 0;
 }
 
-/*
- * Detach all tasks we were using ptrace on.
- */
 void exit_ptrace(struct task_struct *tracer)
 {
-       struct task_struct *p, *n;
-       LIST_HEAD(ptrace_dead);
-
-       write_lock_irq(&tasklist_lock);
-       list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
-               if (__ptrace_detach(tracer, p))
-                       list_add(&p->ptrace_entry, &ptrace_dead);
-       }
-       write_unlock_irq(&tasklist_lock);
+       for (;;) {
+               struct task_struct *tracee = NULL;
 
-       BUG_ON(!list_empty(&tracer->ptraced));
+               read_lock(&tasklist_lock);
+               if (!list_empty(&tracer->ptraced)) {
+                       tracee = list_first_entry(&tracer->ptraced,
+                                       struct task_struct, ptrace_entry);
+                       get_task_struct(tracee);
+               }
+               read_unlock(&tasklist_lock);
+               if (!tracee)
+                       break;
 
-       list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
-               list_del_init(&p->ptrace_entry);
-               release_task(p);
+               ptrace_do_detach(tracee, -1);
+               put_task_struct(tracee);
        }
 }
 

Reply via email to