Merge to Fedora kernel-2.6.18-1.2255_FC5-vs2.0.2.2-rc9 patched with stable patch...
[linux-2.6.git] / kernel / ptrace.c
index ee0b4cb..7dc2944 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_PTRACE
-#include <linux/utrace.h>
-#include <linux/tracehook.h>
-#include <asm/tracehook.h>
-#endif
+/*
+ * ptrace a task: make the debugger its new parent and
+ * move it to the ptrace list.
+ *
+ * Must be called with the tasklist lock write-held.
+ */
+void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
+{
+       BUG_ON(!list_empty(&child->ptrace_list));
+       if (child->parent == new_parent)
+               return;
+       list_add(&child->ptrace_list, &child->parent->ptrace_children);
+       remove_parent(child);
+       child->parent = new_parent;
+       add_parent(child);
+}
+/*
+ * Turn a tracing stop into a normal stop now, since with no tracer there
+ * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
+ * signal sent that would resume the child, but didn't because it was in
+ * TASK_TRACED, resume it now.
+ * Requires that irqs be disabled.
+ */
+void ptrace_untrace(struct task_struct *child)
+{
+       spin_lock(&child->sighand->siglock);
+       if (child->state == TASK_TRACED) {
+               if (child->signal->flags & SIGNAL_STOP_STOPPED) {
+                       child->state = TASK_STOPPED;
+               } else {
+                       signal_wake_up(child, 1);
+               }
+       }
+       spin_unlock(&child->sighand->siglock);
+}
+
+/*
+ * unptrace a task: move it back to its original parent and
+ * remove it from the ptrace list.
+ *
+ * Must be called with the tasklist lock write-held.
+ */
+void __ptrace_unlink(struct task_struct *child)
+{
+       BUG_ON(!child->ptrace);
+
+       child->ptrace = 0;
+       if (!list_empty(&child->ptrace_list)) {
+               list_del_init(&child->ptrace_list);
+               remove_parent(child);
+               child->parent = child->real_parent;
+               add_parent(child);
+       }
+
+       if (child->state == TASK_TRACED)
+               ptrace_untrace(child);
+}
+
+/*
+ * Check that we have indeed attached to the thing..
+ */
+int ptrace_check_attach(struct task_struct *child, int kill)
+{
+       int ret = -ESRCH;
+
+       /*
+        * We take the read lock around doing both checks to close a
+        * possible race where someone else was tracing our child and
+        * detached between these two checks.  After this locked check,
+        * we are sure that this is our traced child and that can only
+        * be changed by us so it's not changing right after this.
+        */
+       read_lock(&tasklist_lock);
+       if ((child->ptrace & PT_PTRACED) && child->parent == current &&
+           (!(child->ptrace & PT_ATTACHED) || child->real_parent != current)
+           && child->signal != NULL) {
+               ret = 0;
+               spin_lock_irq(&child->sighand->siglock);
+               if (child->state == TASK_STOPPED) {
+                       child->state = TASK_TRACED;
+               } else if (child->state != TASK_TRACED && !kill) {
+                       ret = -ESRCH;
+               }
+               spin_unlock_irq(&child->sighand->siglock);
+       }
+       read_unlock(&tasklist_lock);
 
-int getrusage(struct task_struct *, int, struct rusage __user *);
+       if (!ret && !kill) {
+               wait_task_inactive(child);
+       }
 
-//#define PTRACE_DEBUG
+       /* All systems go.. */
+       return ret;
+}
 
 int __ptrace_may_attach(struct task_struct *task)
 {
@@ -71,6 +157,90 @@ int ptrace_may_attach(struct task_struct *task)
        return !err;
 }
 
+int ptrace_attach(struct task_struct *task)
+{
+       int retval;
+
+       retval = -EPERM;
+       if (task->pid <= 1)
+               goto out;
+       if (task->tgid == current->tgid)
+               goto out;
+
+repeat:
+       /*
+        * Nasty, nasty.
+        *
+        * We want to hold both the task-lock and the
+        * tasklist_lock for writing at the same time.
+        * But that's against the rules (tasklist_lock
+        * is taken for reading by interrupts on other
+        * cpu's that may have task_lock).
+        */
+       task_lock(task);
+       local_irq_disable();
+       if (!write_trylock(&tasklist_lock)) {
+               local_irq_enable();
+               task_unlock(task);
+               do {
+                       cpu_relax();
+               } while (!write_can_lock(&tasklist_lock));
+               goto repeat;
+       }
+
+       if (!task->mm)
+               goto bad;
+       /* the same process cannot be attached many times */
+       if (task->ptrace & PT_PTRACED)
+               goto bad;
+       retval = __ptrace_may_attach(task);
+       if (retval)
+               goto bad;
+
+       /* Go */
+       task->ptrace |= PT_PTRACED | ((task->real_parent != current)
+                                     ? PT_ATTACHED : 0);
+       if (capable(CAP_SYS_PTRACE))
+               task->ptrace |= PT_PTRACE_CAP;
+
+       __ptrace_link(task, current);
+
+       force_sig_specific(SIGSTOP, task);
+
+bad:
+       write_unlock_irq(&tasklist_lock);
+       task_unlock(task);
+out:
+       return retval;
+}
+
+static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
+{
+       child->exit_code = data;
+       /* .. re-parent .. */
+       __ptrace_unlink(child);
+       /* .. and wake it up. */
+       if (child->exit_state != EXIT_ZOMBIE)
+               wake_up_process(child);
+}
+
+int ptrace_detach(struct task_struct *child, unsigned int data)
+{
+       if (!valid_signal(data))
+               return -EIO;
+
+       /* Architecture-specific hardware disable .. */
+       ptrace_disable(child);
+
+       write_lock_irq(&tasklist_lock);
+       /* protect against de_thread()->release_task() */
+       if (child->ptrace)
+               __ptrace_detach(child, data);
+       write_unlock_irq(&tasklist_lock);
+
+       return 0;
+}
+
 /*
  * Access another process' address space.
  * Source/target buffer must be kernel space, 
@@ -125,1372 +295,253 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
        return buf - old_buf;
 }
 
-
-#ifndef CONFIG_PTRACE
-
-asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
-{
-       return -ENOSYS;
-}
-
-#else
-
-struct ptrace_state
-{
-       /*
-        * These elements are always available, even when the struct is
-        * awaiting destruction at the next RCU callback point.
-        */
-       struct utrace_attached_engine *engine;
-       struct task_struct *task; /* Target task.  */
-       struct task_struct *parent; /* Whom we report to.  */
-       struct list_head entry; /* Entry on parent->ptracees list.  */
-
-       union {
-               struct rcu_head dead;
-               struct {
-                       u8 options; /* PTRACE_SETOPTIONS bits.  */
-                       unsigned int stopped:1; /* Stopped for report.  */
-                       unsigned int reported:1; /* wait already reported.  */
-                       unsigned int syscall:1; /* Reporting for syscall.  */
-#ifdef PTRACE_SYSEMU
-                       unsigned int sysemu:1; /* PTRACE_SYSEMU in progress. */
-#endif
-                       unsigned int have_eventmsg:1; /* u.eventmsg valid. */
-                       unsigned int cap_sys_ptrace:1; /* Tracer capable.  */
-
-                       union
-                       {
-                               unsigned long eventmsg;
-                               siginfo_t *siginfo;
-                       } u;
-               } live;
-       } u;
-};
-
-static const struct utrace_engine_ops ptrace_utrace_ops; /* Initialized below. */
-
-
-static void
-ptrace_state_link(struct ptrace_state *state)
-{
-       task_lock(state->parent);
-       list_add_rcu(&state->entry, &state->parent->ptracees);
-       task_unlock(state->parent);
-}
-
-static void
-ptrace_state_unlink(struct ptrace_state *state)
-{
-       task_lock(state->parent);
-       list_del_rcu(&state->entry);
-       task_unlock(state->parent);
-}
-
-static int
-ptrace_setup(struct task_struct *target, struct utrace_attached_engine *engine,
-            struct task_struct *parent, u8 options, int cap_sys_ptrace)
-{
-       struct ptrace_state *state = kzalloc(sizeof *state, GFP_USER);
-       if (unlikely(state == NULL))
-               return -ENOMEM;
-
-       state->engine = engine;
-       state->task = target;
-       state->parent = parent;
-       state->u.live.options = options;
-       state->u.live.cap_sys_ptrace = cap_sys_ptrace;
-       ptrace_state_link(state);
-
-       BUG_ON(engine->data != 0);
-       rcu_assign_pointer(engine->data, (unsigned long) state);
-
-       return 0;
-}
-
-static void
-ptrace_state_free(struct rcu_head *rhead)
-{
-       struct ptrace_state *state = container_of(rhead,
-                                                 struct ptrace_state, u.dead);
-       kfree(state);
-}
-
-static void
-ptrace_done(struct ptrace_state *state)
-{
-       INIT_RCU_HEAD(&state->u.dead);
-       call_rcu(&state->u.dead, ptrace_state_free);
-}
-
-/*
- * Update the tracing engine state to match the new ptrace state.
- */
-static void
-ptrace_update(struct task_struct *target, struct utrace_attached_engine *engine,
-             unsigned long flags)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-
-       /*
-        * These events are always reported.
-        */
-       flags |= (UTRACE_EVENT(DEATH) | UTRACE_EVENT(EXEC)
-                 | UTRACE_EVENT_SIGNAL_ALL);
-
-       /*
-        * We always have to examine clone events to check for CLONE_PTRACE.
-        */
-       flags |= UTRACE_EVENT(CLONE);
-
-       /*
-        * PTRACE_SETOPTIONS can request more events.
-        */
-       if (state->u.live.options & PTRACE_O_TRACEEXIT)
-               flags |= UTRACE_EVENT(EXIT);
-       if (state->u.live.options & PTRACE_O_TRACEVFORKDONE)
-               flags |= UTRACE_EVENT(VFORK_DONE);
-
-       /*
-        * ptrace always inhibits normal parent reaping.
-        * But for a corner case we sometimes see the REAP event instead.
-        */
-       flags |= UTRACE_ACTION_NOREAP | UTRACE_EVENT(REAP);
-
-       state->u.live.stopped = (flags & UTRACE_ACTION_QUIESCE) != 0;
-       if (!state->u.live.stopped) {
-               if (!state->u.live.have_eventmsg)
-                       state->u.live.u.siginfo = NULL;
-               if (!(target->flags & PF_EXITING))
-                       target->exit_code = 0;
-       }
-       utrace_set_flags(target, engine, flags);
-}
-
-static int ptrace_traceme(void)
-{
-       struct utrace_attached_engine *engine;
-       int retval;
-
-       engine = utrace_attach(current, (UTRACE_ATTACH_CREATE
-                                        | UTRACE_ATTACH_EXCLUSIVE
-                                        | UTRACE_ATTACH_MATCH_OPS),
-                              &ptrace_utrace_ops, 0UL);
-
-       if (IS_ERR(engine)) {
-               retval = PTR_ERR(engine);
-               if (retval == -EEXIST)
-                       retval = -EPERM;
-       }
-       else {
-               task_lock(current);
-               retval = security_ptrace(current->parent, current);
-               task_unlock(current);
-               if (!retval)
-                       retval = ptrace_setup(current, engine,
-                                             current->parent, 0, 0);
-               if (retval)
-                       utrace_detach(current, engine);
-               else
-                       ptrace_update(current, engine, 0);
-       }
-
-       return retval;
-}
-
-static int ptrace_attach(struct task_struct *task)
-{
-       struct utrace_attached_engine *engine;
-       int retval;
-
-       retval = -EPERM;
-       if (task->pid <= 1)
-               goto bad;
-       if (task->tgid == current->tgid)
-               goto bad;
-       if (!task->mm)          /* kernel threads */
-               goto bad;
-
-       engine = utrace_attach(task, (UTRACE_ATTACH_CREATE
-                                     | UTRACE_ATTACH_EXCLUSIVE
-                                     | UTRACE_ATTACH_MATCH_OPS),
-                              &ptrace_utrace_ops, 0);
-       if (IS_ERR(engine)) {
-               retval = PTR_ERR(engine);
-               if (retval == -EEXIST)
-                       retval = -EPERM;
-               goto bad;
-       }
-
-       if (ptrace_may_attach(task))
-               retval = ptrace_setup(task, engine, current, 0,
-                                     capable(CAP_SYS_PTRACE));
-       if (retval)
-               utrace_detach(task, engine);
-       else {
-               int stopped;
-
-               /* Go */
-               ptrace_update(task, engine, 0);
-               force_sig_specific(SIGSTOP, task);
-
-               spin_lock_irq(&task->sighand->siglock);
-               stopped = (task->state == TASK_STOPPED);
-               spin_unlock_irq(&task->sighand->siglock);
-
-               if (stopped) {
-                       /*
-                        * Do now the regset 0 writeback that we do on every
-                        * stop, since it's never been done.  On register
-                        * window machines, this makes sure the user memory
-                        * backing the register data is up to date.
-                        */
-                       const struct utrace_regset *regset;
-                       regset = utrace_regset(task, engine,
-                                              utrace_native_view(task), 0);
-                       if (regset->writeback)
-                               (*regset->writeback)(task, regset, 1);
-               }
-       }
-
-bad:
-       return retval;
-}
-
-static int ptrace_detach(struct task_struct *task,
-                        struct utrace_attached_engine *engine)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       /*
-        * Clearing ->data before detach makes sure an unrelated task
-        * calling into ptrace_tracer_task won't try to touch stale state.
-        */
-       rcu_assign_pointer(engine->data, 0UL);
-       utrace_detach(task, engine);
-       ptrace_state_unlink(state);
-       ptrace_done(state);
-       return 0;
-}
-
-
-/*
- * This is called when we are exiting.  We must stop all our ptracing.
- */
-void
-ptrace_exit(struct task_struct *tsk)
+int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
 {
-       rcu_read_lock();
-       if (unlikely(!list_empty(&tsk->ptracees))) {
-               struct ptrace_state *state, *next;
+       int copied = 0;
 
-               /*
-                * First detach the utrace layer from all the tasks.
-                * We don't want to hold any locks while calling utrace_detach.
-                */
-               list_for_each_entry_rcu(state, &tsk->ptracees, entry) {
-                       rcu_assign_pointer(state->engine->data, 0UL);
-                       utrace_detach(state->task, state->engine);
-               }
+       while (len > 0) {
+               char buf[128];
+               int this_len, retval;
 
-               /*
-                * Now clear out our list and clean up our data structures.
-                * The task_lock protects our list structure.
-                */
-               task_lock(tsk);
-               list_for_each_entry_safe(state, next, &tsk->ptracees, entry) {
-                       list_del_rcu(&state->entry);
-                       ptrace_done(state);
+               this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+               retval = access_process_vm(tsk, src, buf, this_len, 0);
+               if (!retval) {
+                       if (copied)
+                               break;
+                       return -EIO;
                }
-               task_unlock(tsk);
+               if (copy_to_user(dst, buf, retval))
+                       return -EFAULT;
+               copied += retval;
+               src += retval;
+               dst += retval;
+               len -= retval;                  
        }
-       rcu_read_unlock();
-
-       BUG_ON(!list_empty(&tsk->ptracees));
+       return copied;
 }
 
-static int
-ptrace_induce_signal(struct task_struct *target,
-                    struct utrace_attached_engine *engine,
-                    long signr)
+int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
 {
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
+       int copied = 0;
 
-       if (signr == 0)
-               return 0;
+       while (len > 0) {
+               char buf[128];
+               int this_len, retval;
 
-       if (!valid_signal(signr))
-               return -EIO;
-
-       if (state->u.live.syscall) {
-               /*
-                * This is the traditional ptrace behavior when given
-                * a signal to resume from a syscall tracing stop.
-                */
-               send_sig(signr, target, 1);
-       }
-       else if (!state->u.live.have_eventmsg && state->u.live.u.siginfo) {
-               siginfo_t *info = state->u.live.u.siginfo;
-
-               /* Update the siginfo structure if the signal has
-                  changed.  If the debugger wanted something
-                  specific in the siginfo structure then it should
-                  have updated *info via PTRACE_SETSIGINFO.  */
-               if (signr != info->si_signo) {
-                       info->si_signo = signr;
-                       info->si_errno = 0;
-                       info->si_code = SI_USER;
-                       info->si_pid = current->pid;
-                       info->si_uid = current->uid;
+               this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+               if (copy_from_user(buf, src, this_len))
+                       return -EFAULT;
+               retval = access_process_vm(tsk, dst, buf, this_len, 1);
+               if (!retval) {
+                       if (copied)
+                               break;
+                       return -EIO;
                }
-
-               return utrace_inject_signal(target, engine,
-                                           UTRACE_ACTION_RESUME, info, NULL);
+               copied += retval;
+               src += retval;
+               dst += retval;
+               len -= retval;                  
        }
-
-       return 0;
+       return copied;
 }
 
-fastcall int
-ptrace_regset_access(struct task_struct *target,
-                    struct utrace_attached_engine *engine,
-                    const struct utrace_regset_view *view,
-                    int setno, unsigned long offset, unsigned int size,
-                    void __user *data, int write)
+static int ptrace_setoptions(struct task_struct *child, long data)
 {
-       const struct utrace_regset *regset = utrace_regset(target, engine,
-                                                          view, setno);
-       int ret;
-
-       if (unlikely(regset == NULL))
-               return -EIO;
+       child->ptrace &= ~PT_TRACE_MASK;
 
-       if (size == (unsigned int) -1)
-               size = regset->size * regset->n;
+       if (data & PTRACE_O_TRACESYSGOOD)
+               child->ptrace |= PT_TRACESYSGOOD;
 
-       if (write) {
-               if (!access_ok(VERIFY_READ, data, size))
-                       ret = -EIO;
-               else
-                       ret = (*regset->set)(target, regset,
-                                            offset, size, NULL, data);
-       }
-       else {
-               if (!access_ok(VERIFY_WRITE, data, size))
-                       ret = -EIO;
-               else
-                       ret = (*regset->get)(target, regset,
-                                            offset, size, NULL, data);
-       }
-
-       return ret;
-}
+       if (data & PTRACE_O_TRACEFORK)
+               child->ptrace |= PT_TRACE_FORK;
 
-fastcall int
-ptrace_onereg_access(struct task_struct *target,
-                    struct utrace_attached_engine *engine,
-                    const struct utrace_regset_view *view,
-                    int setno, unsigned long regno,
-                    void __user *data, int write)
-{
-       const struct utrace_regset *regset = utrace_regset(target, engine,
-                                                          view, setno);
-       unsigned int pos;
-       int ret;
+       if (data & PTRACE_O_TRACEVFORK)
+               child->ptrace |= PT_TRACE_VFORK;
 
-       if (unlikely(regset == NULL))
-               return -EIO;
+       if (data & PTRACE_O_TRACECLONE)
+               child->ptrace |= PT_TRACE_CLONE;
 
-       if (regno < regset->bias || regno >= regset->bias + regset->n)
-               return -EINVAL;
+       if (data & PTRACE_O_TRACEEXEC)
+               child->ptrace |= PT_TRACE_EXEC;
 
-       pos = (regno - regset->bias) * regset->size;
+       if (data & PTRACE_O_TRACEVFORKDONE)
+               child->ptrace |= PT_TRACE_VFORK_DONE;
 
-       if (write) {
-               if (!access_ok(VERIFY_READ, data, regset->size))
-                       ret = -EIO;
-               else
-                       ret = (*regset->set)(target, regset, pos, regset->size,
-                                            NULL, data);
-       }
-       else {
-               if (!access_ok(VERIFY_WRITE, data, regset->size))
-                       ret = -EIO;
-               else
-                       ret = (*regset->get)(target, regset, pos, regset->size,
-                                            NULL, data);
-       }
+       if (data & PTRACE_O_TRACEEXIT)
+               child->ptrace |= PT_TRACE_EXIT;
 
-       return ret;
+       return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
 }
 
-fastcall int
-ptrace_layout_access(struct task_struct *target,
-                    struct utrace_attached_engine *engine,
-                    const struct utrace_regset_view *view,
-                    const struct ptrace_layout_segment layout[],
-                    unsigned long addr, unsigned int size,
-                    void __user *udata, void *kdata, int write)
+static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data)
 {
-       const struct ptrace_layout_segment *seg;
-       int ret = -EIO;
+       siginfo_t lastinfo;
+       int error = -ESRCH;
 
-       if (kdata == NULL &&
-           !access_ok(write ? VERIFY_READ : VERIFY_WRITE, udata, size))
-               return -EIO;
-
-       seg = layout;
-       do {
-               unsigned int pos, n;
-
-               while (addr >= seg->end && seg->end != 0)
-                       ++seg;
-
-               if (addr < seg->start || addr >= seg->end)
-                       return -EIO;
-
-               pos = addr - seg->start + seg->offset;
-               n = min(size, seg->end - (unsigned int) addr);
-
-               if (unlikely(seg->regset == (unsigned int) -1)) {
-                       /*
-                        * This is a no-op/zero-fill portion of struct user.
-                        */
-                       ret = 0;
-                       if (!write) {
-                               if (kdata)
-                                       memset(kdata, 0, n);
-                               else if (clear_user(udata, n))
-                                       ret = -EFAULT;
-                       }
-               }
-               else {
-                       unsigned int align;
-                       const struct utrace_regset *regset = utrace_regset(
-                               target, engine, view, seg->regset);
-                       if (unlikely(regset == NULL))
-                               return -EIO;
-
-                       /*
-                        * A ptrace compatibility layout can do a misaligned
-                        * regset access, e.g. word access to larger data.
-                        * An arch's compat layout can be this way only if
-                        * it is actually ok with the regset code despite the
-                        * regset->align setting.
-                        */
-                       align = min(regset->align, size);
-                       if ((pos & (align - 1))
-                           || pos >= regset->n * regset->size)
-                               return -EIO;
-
-                       if (write)
-                               ret = (*regset->set)(target, regset,
-                                                    pos, n, kdata, udata);
-                       else
-                               ret = (*regset->get)(target, regset,
-                                                    pos, n, kdata, udata);
+       read_lock(&tasklist_lock);
+       if (likely(child->sighand != NULL)) {
+               error = -EINVAL;
+               spin_lock_irq(&child->sighand->siglock);
+               if (likely(child->last_siginfo != NULL)) {
+                       lastinfo = *child->last_siginfo;
+                       error = 0;
                }
-
-               if (kdata)
-                       kdata += n;
-               else
-                       udata += n;
-               addr += n;
-               size -= n;
-       } while (ret == 0 && size > 0);
-
-       return ret;
+               spin_unlock_irq(&child->sighand->siglock);
+       }
+       read_unlock(&tasklist_lock);
+       if (!error)
+               return copy_siginfo_to_user(data, &lastinfo);
+       return error;
 }
 
-
-static int
-ptrace_start(long pid, long request,
-            struct task_struct **childp,
-            struct utrace_attached_engine **enginep,
-            struct ptrace_state **statep)
-
+static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
 {
-       struct task_struct *child;
-       struct utrace_attached_engine *engine;
-       struct ptrace_state *state;
-       int ret;
+       siginfo_t newinfo;
+       int error = -ESRCH;
 
-       if (request == PTRACE_TRACEME)
-               return ptrace_traceme();
+       if (copy_from_user(&newinfo, data, sizeof (siginfo_t)))
+               return -EFAULT;
 
-       ret = -ESRCH;
        read_lock(&tasklist_lock);
-       child = find_task_by_pid(pid);
-       if (child)
-               get_task_struct(child);
-       read_unlock(&tasklist_lock);
-#ifdef PTRACE_DEBUG
-       printk("ptrace pid %ld => %p\n", pid, child);
-#endif
-       if (!child)
-               goto out;
-
-       ret = -EPERM;
-       if (pid == 1)           /* you may not mess with init */
-               goto out_tsk;
-
-       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT))
-               goto out_tsk;
-
-       if (request == PTRACE_ATTACH) {
-               ret = ptrace_attach(child);
-               goto out_tsk;
-       }
-
-       engine = utrace_attach(child, UTRACE_ATTACH_MATCH_OPS,
-                              &ptrace_utrace_ops, 0);
-       ret = -ESRCH;
-       if (IS_ERR(engine) || engine == NULL)
-               goto out_tsk;
-       rcu_read_lock();
-       state = rcu_dereference((struct ptrace_state *) engine->data);
-       if (state == NULL || state->parent != current) {
-               rcu_read_unlock();
-               goto out_tsk;
-       }
-       rcu_read_unlock();
-
-       /*
-        * Traditional ptrace behavior demands that the target already be
-        * quiescent, but not dead.
-        */
-       if (request != PTRACE_KILL && !state->u.live.stopped) {
-#ifdef PTRACE_DEBUG
-               printk("%d not stopped (%lx)\n", child->pid, child->state);
-#endif
-               if (child->state != TASK_STOPPED)
-                       goto out_tsk;
-               utrace_set_flags(child, engine,
-                                engine->flags | UTRACE_ACTION_QUIESCE);
+       if (likely(child->sighand != NULL)) {
+               error = -EINVAL;
+               spin_lock_irq(&child->sighand->siglock);
+               if (likely(child->last_siginfo != NULL)) {
+                       *child->last_siginfo = newinfo;
+                       error = 0;
+               }
+               spin_unlock_irq(&child->sighand->siglock);
        }
-
-       /*
-        * We do this for all requests to match traditional ptrace behavior.
-        * If the machine state synchronization done at context switch time
-        * includes e.g. writing back to user memory, we want to make sure
-        * that has finished before a PTRACE_PEEKDATA can fetch the results.
-        * On most machines, only regset data is affected by context switch
-        * and calling utrace_regset later on will take care of that, so
-        * this is superfluous.
-        *
-        * To do this purely in utrace terms, we could do:
-        *  (void) utrace_regset(child, engine, utrace_native_view(child), 0);
-        */
-       wait_task_inactive(child);
-
-       if (child->exit_state)
-               goto out_tsk;
-
-       *childp = child;
-       *enginep = engine;
-       *statep = state;
-       return -EIO;
-
-out_tsk:
-       put_task_struct(child);
-out:
-       return ret;
+       read_unlock(&tasklist_lock);
+       return error;
 }
 
-static int
-ptrace_common(long request, struct task_struct *child,
-             struct utrace_attached_engine *engine,
-             struct ptrace_state *state,
-             unsigned long addr, long data)
+int ptrace_request(struct task_struct *child, long request,
+                  long addr, long data)
 {
-       unsigned long flags;
        int ret = -EIO;
 
        switch (request) {
-       case PTRACE_DETACH:
-               /*
-                * Detach a process that was attached.
-                */
-               ret = ptrace_induce_signal(child, engine, data);
-               if (!ret)
-                       ret = ptrace_detach(child, engine);
-               break;
-
-               /*
-                * These are the operations that resume the child running.
-                */
-       case PTRACE_KILL:
-               data = SIGKILL;
-       case PTRACE_CONT:
-       case PTRACE_SYSCALL:
-#ifdef PTRACE_SYSEMU
-       case PTRACE_SYSEMU:
-       case PTRACE_SYSEMU_SINGLESTEP:
-#endif
-#ifdef PTRACE_SINGLEBLOCK
-       case PTRACE_SINGLEBLOCK:
-# ifdef ARCH_HAS_BLOCK_STEP
-               if (! ARCH_HAS_BLOCK_STEP)
-# endif
-                       if (request == PTRACE_SINGLEBLOCK)
-                               break;
-#endif
-       case PTRACE_SINGLESTEP:
-#ifdef ARCH_HAS_SINGLE_STEP
-               if (! ARCH_HAS_SINGLE_STEP)
-#endif
-                       if (request == PTRACE_SINGLESTEP
-#ifdef PTRACE_SYSEMU_SINGLESTEP
-                           || request == PTRACE_SYSEMU_SINGLESTEP
-#endif
-                               )
-                               break;
-
-               ret = ptrace_induce_signal(child, engine, data);
-               if (ret)
-                       break;
-
-
-               /*
-                * Reset the action flags without QUIESCE, so it resumes.
-                */
-               flags = 0;
-#ifdef PTRACE_SYSEMU
-               state->u.live.sysemu = (request == PTRACE_SYSEMU_SINGLESTEP
-                                       || request == PTRACE_SYSEMU);
-#endif
-               if (request == PTRACE_SINGLESTEP
-#ifdef PTRACE_SYSEMU
-                   || request == PTRACE_SYSEMU_SINGLESTEP
-#endif
-                       )
-                       flags |= UTRACE_ACTION_SINGLESTEP;
-#ifdef PTRACE_SINGLEBLOCK
-               else if (request == PTRACE_SINGLEBLOCK)
-                       flags |= UTRACE_ACTION_BLOCKSTEP;
-#endif
-               if (request == PTRACE_SYSCALL)
-                       flags |= UTRACE_EVENT_SYSCALL;
-#ifdef PTRACE_SYSEMU
-               else if (request == PTRACE_SYSEMU
-                        || request == PTRACE_SYSEMU_SINGLESTEP)
-                       flags |= UTRACE_EVENT(SYSCALL_ENTRY);
-#endif
-               ptrace_update(child, engine, flags);
-               ret = 0;
-               break;
-
 #ifdef PTRACE_OLDSETOPTIONS
        case PTRACE_OLDSETOPTIONS:
 #endif
        case PTRACE_SETOPTIONS:
-               ret = -EINVAL;
-               if (data & ~PTRACE_O_MASK)
-                       break;
-               state->u.live.options = data;
-               ptrace_update(child, engine, UTRACE_ACTION_QUIESCE);
-               ret = 0;
-               break;
-       }
-
-       return ret;
-}
-
-
-asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
-{
-       struct task_struct *child;
-       struct utrace_attached_engine *engine;
-       struct ptrace_state *state;
-       long ret, val;
-
-#ifdef PTRACE_DEBUG
-       printk("%d sys_ptrace(%ld, %ld, %lx, %lx)\n",
-              current->pid, request, pid, addr, data);
-#endif
-
-       ret = ptrace_start(pid, request, &child, &engine, &state);
-       if (ret != -EIO)
-               goto out;
-
-       val = 0;
-       ret = arch_ptrace(&request, child, engine, addr, data, &val);
-       if (ret != -ENOSYS) {
-               if (ret == 0) {
-                       ret = val;
-                       force_successful_syscall_return();
-               }
-               goto out_tsk;
-       }
-
-       switch (request) {
-       default:
-               ret = ptrace_common(request, child, engine, state, addr, data);
+               ret = ptrace_setoptions(child, data);
                break;
-
-       case PTRACE_PEEKTEXT: /* read word at location addr. */
-       case PTRACE_PEEKDATA: {
-               unsigned long tmp;
-               int copied;
-
-               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
-               ret = -EIO;
-               if (copied != sizeof(tmp))
-                       break;
-               ret = put_user(tmp, (unsigned long __user *) data);
-               break;
-       }
-
-       case PTRACE_POKETEXT: /* write the word at location addr. */
-       case PTRACE_POKEDATA:
-               ret = 0;
-               if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
-                       break;
-               ret = -EIO;
-               break;
-
        case PTRACE_GETEVENTMSG:
-               ret = put_user(state->u.live.have_eventmsg
-                              ? state->u.live.u.eventmsg : 0L,
-                              (unsigned long __user *) data);
+               ret = put_user(child->ptrace_message, (unsigned long __user *) data);
                break;
        case PTRACE_GETSIGINFO:
-               ret = -EINVAL;
-               if (!state->u.live.have_eventmsg && state->u.live.u.siginfo)
-                       ret = copy_siginfo_to_user((siginfo_t __user *) data,
-                                                  state->u.live.u.siginfo);
+               ret = ptrace_getsiginfo(child, (siginfo_t __user *) data);
                break;
        case PTRACE_SETSIGINFO:
-               ret = -EINVAL;
-               if (!state->u.live.have_eventmsg && state->u.live.u.siginfo
-                   && copy_from_user(state->u.live.u.siginfo,
-                                     (siginfo_t __user *) data,
-                                     sizeof(siginfo_t)))
-                       ret = -EFAULT;
+               ret = ptrace_setsiginfo(child, (siginfo_t __user *) data);
                break;
-       }
-
-out_tsk:
-       put_task_struct(child);
-out:
-#ifdef PTRACE_DEBUG
-       printk("%d ptrace -> %x\n", current->pid, ret);
-#endif
-       return ret;
-}
-
-
-#ifdef CONFIG_COMPAT
-#include <linux/compat.h>
-
-asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
-                                 compat_ulong_t addr, compat_long_t cdata)
-{
-       const unsigned long data = (unsigned long) (compat_ulong_t) cdata;
-       struct task_struct *child;
-       struct utrace_attached_engine *engine;
-       struct ptrace_state *state;
-       compat_long_t ret, val;
-
-#ifdef PTRACE_DEBUG
-       printk("%d compat_sys_ptrace(%d, %d, %x, %x)\n",
-              current->pid, request, pid, addr, cdata);
-#endif
-       ret = ptrace_start(pid, request, &child, &engine, &state);
-       if (ret != -EIO)
-               goto out;
-
-       val = 0;
-       ret = arch_compat_ptrace(&request, child, engine, addr, cdata, &val);
-       if (ret != -ENOSYS) {
-               if (ret == 0) {
-                       ret = val;
-                       force_successful_syscall_return();
-               }
-               goto out_tsk;
-       }
-
-       switch (request) {
        default:
-               ret = ptrace_common(request, child, engine, state, addr, data);
-               break;
-
-       case PTRACE_PEEKTEXT: /* read word at location addr. */
-       case PTRACE_PEEKDATA: {
-               compat_ulong_t tmp;
-               int copied;
-
-               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
-               ret = -EIO;
-               if (copied != sizeof(tmp))
-                       break;
-               ret = put_user(tmp, (compat_ulong_t __user *) data);
                break;
        }
 
-       case PTRACE_POKETEXT: /* write the word at location addr. */
-       case PTRACE_POKEDATA:
-               ret = 0;
-               if (access_process_vm(child, addr, &cdata, sizeof(cdata), 1) == sizeof(cdata))
-                       break;
-               ret = -EIO;
-               break;
-
-       case PTRACE_GETEVENTMSG:
-               ret = put_user(state->u.live.have_eventmsg
-                              ? state->u.live.u.eventmsg : 0L,
-                              (compat_long_t __user *) data);
-               break;
-       case PTRACE_GETSIGINFO:
-               ret = -EINVAL;
-               if (!state->u.live.have_eventmsg && state->u.live.u.siginfo)
-                       ret = copy_siginfo_to_user32(
-                               (struct compat_siginfo __user *) data,
-                               state->u.live.u.siginfo);
-               break;
-       case PTRACE_SETSIGINFO:
-               ret = -EINVAL;
-               if (!state->u.live.have_eventmsg && state->u.live.u.siginfo
-                   && copy_siginfo_from_user32(
-                           state->u.live.u.siginfo,
-                           (struct compat_siginfo __user *) data))
-                       ret = -EFAULT;
-               break;
-       }
-
-out_tsk:
-       put_task_struct(child);
-out:
-#ifdef PTRACE_DEBUG
-       printk("%d ptrace -> %x\n", current->pid, ret);
-#endif
        return ret;
 }
-#endif
-
 
-/*
- * We're called with tasklist_lock held for reading.
- * If we return -ECHILD or zero, next_thread(tsk) must still be valid to use.
- * If we return another error code, or a successful PID value, we
- * release tasklist_lock first.
+/**
+ * ptrace_traceme  --  helper for PTRACE_TRACEME
+ *
+ * Performs checks and sets PT_PTRACED.
+ * Should be used by all ptrace implementations for PTRACE_TRACEME.
  */
-int
-ptrace_do_wait(struct task_struct *tsk,
-              pid_t pid, int options, struct siginfo __user *infop,
-              int __user *stat_addr, struct rusage __user *rusagep)
+int ptrace_traceme(void)
 {
-       struct ptrace_state *state;
-       struct task_struct *p;
-       int err = -ECHILD;
-       int why, status;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(state, &tsk->ptracees, entry) {
-               p = state->task;
-
-               if (pid > 0) {
-                       if (p->pid != pid)
-                               continue;
-               } else if (!pid) {
-                       if (process_group(p) != process_group(current))
-                               continue;
-               } else if (pid != -1) {
-                       if (process_group(p) != -pid)
-                               continue;
-               }
-               if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
-                   && !(options & __WALL))
-                       continue;
-               if (security_task_wait(p))
-                       continue;
-
-               err = 0;
-               if (state->u.live.reported)
-                       continue;
-
-               if (state->u.live.stopped)
-                       goto found;
-               if ((p->state & (TASK_TRACED | TASK_STOPPED))
-                   && (p->signal->flags & SIGNAL_STOP_STOPPED))
-                       goto found;
-               if (p->exit_state == EXIT_ZOMBIE) {
-                       if (!likely(options & WEXITED))
-                               continue;
-                       if (delay_group_leader(p))
-                               continue;
-                       goto found;
-               }
-               // XXX should handle WCONTINUED
-       }
-       rcu_read_unlock();
-       return err;
-
-found:
-       rcu_read_unlock();
-
-       BUG_ON(state->parent != tsk);
-
-       if (p->exit_state) {
-               if (unlikely(p->parent == state->parent))
-                       /*
-                        * This is our natural child we were ptracing.
-                        * When it dies it detaches (see ptrace_report_death).
-                        * So we're seeing it here in a race.  When it
-                        * finishes detaching it will become reapable in
-                        * the normal wait_task_zombie path instead.
-                        */
-                       return 0;
-               if ((p->exit_code & 0x7f) == 0) {
-                       why = CLD_EXITED;
-                       status = p->exit_code >> 8;
-               } else {
-                       why = (p->exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED;
-                       status = p->exit_code & 0xff;
-               }
-       }
-       else {
-               why = CLD_TRAPPED;
-               status = (p->exit_code << 8) | 0x7f;
-       }
+       int ret = -EPERM;
 
        /*
-        * At this point we are committed to a successful return
-        * or a user error return.  Release the tasklist_lock.
+        * Are we already being traced?
         */
-       read_unlock(&tasklist_lock);
-
-       if (rusagep)
-               err = getrusage(p, RUSAGE_BOTH, rusagep);
-       if (infop) {
-               if (!err)
-                       err = put_user(SIGCHLD, &infop->si_signo);
-               if (!err)
-                       err = put_user(0, &infop->si_errno);
-               if (!err)
-                       err = put_user((short)why, &infop->si_code);
-               if (!err)
-                       err = put_user(p->pid, &infop->si_pid);
-               if (!err)
-                       err = put_user(p->uid, &infop->si_uid);
-               if (!err)
-                       err = put_user(status, &infop->si_status);
-       }
-       if (!err && stat_addr)
-               err = put_user(status, stat_addr);
-
-       if (!err) {
-               struct utrace *utrace;
-
-               err = p->pid;
-
+       task_lock(current);
+       if (!(current->ptrace & PT_PTRACED)) {
+               ret = security_ptrace(current->parent, current);
                /*
-                * If this was a non-death report, the child might now be
-                * detaching on death in the same race possible in the
-                * p->exit_state check above.  So check for p->utrace being
-                * NULL, then we don't need to update the state any more.
+                * Set the ptrace bit in the process ptrace flags.
                 */
-               rcu_read_lock();
-               utrace = rcu_dereference(p->utrace);
-               if (likely(utrace != NULL)) {
-                       utrace_lock(utrace);
-                       if (unlikely(state->u.live.reported))
-                               /*
-                                * Another thread in the group got here
-                                * first and reaped it before we locked.
-                                */
-                               err = -ERESTARTNOINTR;
-                       state->u.live.reported = 1;
-                       utrace_unlock(utrace);
-               }
-               rcu_read_unlock();
-
-               if (err > 0 && why != CLD_TRAPPED)
-                       ptrace_detach(p, state->engine);
+               if (!ret)
+                       current->ptrace |= PT_PTRACED;
        }
-
-       return err;
+       task_unlock(current);
+       return ret;
 }
 
-static void
-do_notify(struct task_struct *tsk, struct task_struct *parent, int why)
+/**
+ * ptrace_get_task_struct  --  grab a task struct reference for ptrace
+ * @pid:       process id to grab a task_struct reference of
+ *
+ * This function is a helper for ptrace implementations.  It checks
+ * permissions and then grabs a task struct for use of the actual
+ * ptrace implementation.
+ *
+ * Returns the task_struct for @pid or an ERR_PTR() on failure.
+ */
+struct task_struct *ptrace_get_task_struct(pid_t pid)
 {
-       struct siginfo info;
-       unsigned long flags;
-       struct sighand_struct *sighand;
-       int sa_mask;
-
-       info.si_signo = SIGCHLD;
-       info.si_errno = 0;
-       info.si_pid = tsk->pid;
-       info.si_uid = tsk->uid;
-
-       /* FIXME: find out whether or not this is supposed to be c*time. */
-       info.si_utime = cputime_to_jiffies(tsk->utime);
-       info.si_stime = cputime_to_jiffies(tsk->stime);
-
-       sa_mask = SA_NOCLDSTOP;
-       info.si_code = why;
-       info.si_status = tsk->exit_code & 0x7f;
-       if (why == CLD_CONTINUED)
-               info.si_status = SIGCONT;
-       else if (why == CLD_STOPPED)
-               info.si_status = tsk->signal->group_exit_code & 0x7f;
-       else if (why == CLD_EXITED) {
-               sa_mask = SA_NOCLDWAIT;
-               if (tsk->exit_code & 0x80)
-                       info.si_code = CLD_DUMPED;
-               else if (tsk->exit_code & 0x7f)
-                       info.si_code = CLD_KILLED;
-               else {
-                       info.si_code = CLD_EXITED;
-                       info.si_status = tsk->exit_code >> 8;
-               }
-       }
+       struct task_struct *child;
 
-       sighand = parent->sighand;
-       spin_lock_irqsave(&sighand->siglock, flags);
-       if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
-           !(sighand->action[SIGCHLD-1].sa.sa_flags & sa_mask))
-               __group_send_sig_info(SIGCHLD, &info, parent);
        /*
-        * Even if SIGCHLD is not generated, we must wake up wait4 calls.
+        * Tracing init is not allowed.
         */
-       wake_up_interruptible_sync(&parent->signal->wait_chldexit);
-       spin_unlock_irqrestore(&sighand->siglock, flags);
+       if (pid == 1)
+               return ERR_PTR(-EPERM);
+
+       read_lock(&tasklist_lock);
+       child = find_task_by_pid(pid);
+       if (child)
+               get_task_struct(child);
+       read_unlock(&tasklist_lock);
+       if (!child)
+               return ERR_PTR(-ESRCH);
+       return child;
 }
 
-static u32
-ptrace_report(struct utrace_attached_engine *engine, struct task_struct *tsk,
-             int code)
+#ifndef __ARCH_SYS_PTRACE
+asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
 {
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       const struct utrace_regset *regset;
-
-#ifdef PTRACE_DEBUG
-       printk("%d ptrace_report %d engine %p state %p code %x parent %d (%p)\n",
-              current->pid, tsk->pid, engine, state, code,
-              state->parent->pid, state->parent);
-       if (!state->u.live.have_eventmsg && state->u.live.u.siginfo) {
-               const siginfo_t *si = state->u.live.u.siginfo;
-               printk("  si %d code %x errno %d addr %p\n",
-                      si->si_signo, si->si_code, si->si_errno,
-                      si->si_addr);
-       }
-#endif
-
-       /*
-        * Set our QUIESCE flag right now, before notifying the tracer.
-        * We do this before setting state->u.live.stopped rather than
-        * by using UTRACE_ACTION_NEWSTATE in our return value, to
-        * ensure that the tracer can't get the notification and then
-        * try to resume us with PTRACE_CONT before we set the flag.
-        */
-       utrace_set_flags(tsk, engine, engine->flags | UTRACE_ACTION_QUIESCE);
+       struct task_struct *child;
+       long ret;
 
        /*
-        * If regset 0 has a writeback call, do it now.  On register window
-        * machines, this makes sure the user memory backing the register
-        * data is up to date by the time wait_task_inactive returns to
-        * ptrace_start in our tracer doing a PTRACE_PEEKDATA or the like.
+        * This lock_kernel fixes a subtle race with suid exec
         */
-       regset = utrace_regset(tsk, engine, utrace_native_view(tsk), 0);
-       if (regset->writeback)
-               (*regset->writeback)(tsk, regset, 0);
-
-       state->u.live.stopped = 1;
-       state->u.live.reported = 0;
-       tsk->exit_code = code;
-       do_notify(tsk, state->parent, CLD_TRAPPED);
-
-#ifdef PTRACE_DEBUG
-       printk("%d ptrace_report quiescing exit_code %x\n",
-              current->pid, current->exit_code);
-#endif
-
-       return UTRACE_ACTION_RESUME;
-}
-
-static inline u32
-ptrace_event(struct utrace_attached_engine *engine, struct task_struct *tsk,
-            int event)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       state->u.live.syscall = 0;
-       return ptrace_report(engine, tsk, (event << 8) | SIGTRAP);
-}
-
-
-static u32
-ptrace_report_death(struct utrace_attached_engine *engine,
-                   struct task_struct *tsk)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-
-       if (tsk->parent == state->parent) {
-               /*
-                * This is a natural child, so we detach and let the normal
-                * reporting happen once our NOREAP action is gone.  But
-                * first, generate a SIGCHLD for those cases where normal
-                * behavior won't.  A ptrace'd child always generates SIGCHLD.
-                */
-               if (tsk->exit_signal == -1 || !thread_group_empty(tsk))
-                       do_notify(tsk, state->parent, CLD_EXITED);
-               ptrace_state_unlink(state);
-               rcu_assign_pointer(engine->data, 0UL);
-               ptrace_done(state);
-               return UTRACE_ACTION_DETACH;
-       }
-
-       state->u.live.reported = 0;
-       do_notify(tsk, state->parent, CLD_EXITED);
-       return UTRACE_ACTION_RESUME;
-}
-
-/*
- * We get this only in the case where our UTRACE_ACTION_NOREAP was ignored.
- * That happens solely when a non-leader exec reaps the old leader.
- */
-static void
-ptrace_report_reap(struct utrace_attached_engine *engine,
-                  struct task_struct *tsk)
-{
-       struct ptrace_state *state;
-       rcu_read_lock();
-       state = rcu_dereference((struct ptrace_state *) engine->data);
-       if (state != NULL) {
-               ptrace_state_unlink(state);
-               rcu_assign_pointer(engine->data, 0UL);
-               ptrace_done(state);
-       }
-       rcu_read_unlock();
-}
-
-
-static u32
-ptrace_report_clone(struct utrace_attached_engine *engine,
-                   struct task_struct *parent,
-                   unsigned long clone_flags, struct task_struct *child)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       struct utrace_attached_engine *child_engine;
-       int event = PTRACE_EVENT_FORK;
-       int option = PTRACE_O_TRACEFORK;
-
-#ifdef PTRACE_DEBUG
-       printk("%d (%p) engine %p ptrace_report_clone child %d (%p) fl %lx\n",
-              parent->pid, parent, engine, child->pid, child, clone_flags);
-#endif
-
-       if (clone_flags & CLONE_UNTRACED)
+       lock_kernel();
+       if (request == PTRACE_TRACEME) {
+               ret = ptrace_traceme();
                goto out;
-
-       if (clone_flags & CLONE_VFORK) {
-               event = PTRACE_EVENT_VFORK;
-               option = PTRACE_O_TRACEVFORK;
-       }
-       else if ((clone_flags & CSIGNAL) != SIGCHLD) {
-               event = PTRACE_EVENT_CLONE;
-               option = PTRACE_O_TRACECLONE;
        }
 
-       if (!(clone_flags & CLONE_PTRACE) && !(state->u.live.options & option))
+       child = ptrace_get_task_struct(pid);
+       if (IS_ERR(child)) {
+               ret = PTR_ERR(child);
                goto out;
-
-       child_engine = utrace_attach(child, (UTRACE_ATTACH_CREATE
-                                            | UTRACE_ATTACH_EXCLUSIVE
-                                            | UTRACE_ATTACH_MATCH_OPS),
-                                    &ptrace_utrace_ops, 0UL);
-       if (unlikely(IS_ERR(child_engine))) {
-               BUG_ON(PTR_ERR(child_engine) != -ENOMEM);
-               printk(KERN_ERR
-                      "ptrace out of memory, lost child %d of %d",
-                      child->pid, parent->pid);
-       }
-       else {
-               int ret = ptrace_setup(child, child_engine,
-                                      state->parent,
-                                      state->u.live.options,
-                                      state->u.live.cap_sys_ptrace);
-               if (unlikely(ret != 0)) {
-                       BUG_ON(ret != -ENOMEM);
-                       printk(KERN_ERR
-                              "ptrace out of memory, lost child %d of %d",
-                              child->pid, parent->pid);
-                       utrace_detach(child, child_engine);
-               }
-               else {
-                       sigaddset(&child->pending.signal, SIGSTOP);
-                       set_tsk_thread_flag(child, TIF_SIGPENDING);
-                       ptrace_update(child, child_engine, 0);
-               }
-       }
-
-       if (state->u.live.options & option) {
-               state->u.live.have_eventmsg = 1;
-               state->u.live.u.eventmsg = child->pid;
-               return ptrace_event(engine, parent, event);
        }
 
-out:
-       return UTRACE_ACTION_RESUME;
-}
-
-
-static u32
-ptrace_report_vfork_done(struct utrace_attached_engine *engine,
-                        struct task_struct *parent, pid_t child_pid)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       state->u.live.have_eventmsg = 1;
-       state->u.live.u.eventmsg = child_pid;
-       return ptrace_event(engine, parent, PTRACE_EVENT_VFORK_DONE);
-}
-
-
-static u32
-ptrace_report_signal(struct utrace_attached_engine *engine,
-                    struct task_struct *tsk, struct pt_regs *regs,
-                    u32 action, siginfo_t *info,
-                    const struct k_sigaction *orig_ka,
-                    struct k_sigaction *return_ka)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       int signo = info == NULL ? SIGTRAP : info->si_signo;
-       state->u.live.syscall = 0;
-       state->u.live.have_eventmsg = 0;
-       state->u.live.u.siginfo = info;
-       return ptrace_report(engine, tsk, signo) | UTRACE_SIGNAL_IGN;
-}
-
-static u32
-ptrace_report_jctl(struct utrace_attached_engine *engine,
-                  struct task_struct *tsk, int type)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       do_notify(tsk, state->parent, type);
-       return UTRACE_JCTL_NOSIGCHLD;
-}
-
-static u32
-ptrace_report_exec(struct utrace_attached_engine *engine,
-                  struct task_struct *tsk,
-                  const struct linux_binprm *bprm,
-                  struct pt_regs *regs)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       if (state->u.live.options & PTRACE_O_TRACEEXEC)
-               return ptrace_event(engine, tsk, PTRACE_EVENT_EXEC);
-       state->u.live.syscall = 0;
-       return ptrace_report(engine, tsk, SIGTRAP);
-}
-
-static u32
-ptrace_report_syscall(struct utrace_attached_engine *engine,
-                     struct task_struct *tsk, struct pt_regs *regs,
-                     int entry)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-#ifdef PTRACE_SYSEMU
-       if (entry && state->u.live.sysemu)
-               tracehook_abort_syscall(regs);
-#endif
-       state->u.live.syscall = 1;
-       return ptrace_report(engine, tsk,
-                            ((state->u.live.options & PTRACE_O_TRACESYSGOOD)
-                             ? 0x80 : 0) | SIGTRAP);
-}
-
-static u32
-ptrace_report_syscall_entry(struct utrace_attached_engine *engine,
-                           struct task_struct *tsk, struct pt_regs *regs)
-{
-       return ptrace_report_syscall(engine, tsk, regs, 1);
-}
-
-static u32
-ptrace_report_syscall_exit(struct utrace_attached_engine *engine,
-                           struct task_struct *tsk, struct pt_regs *regs)
-{
-       return ptrace_report_syscall(engine, tsk, regs, 0);
-}
-
-static u32
-ptrace_report_exit(struct utrace_attached_engine *engine,
-                  struct task_struct *tsk, long orig_code, long *code)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       state->u.live.have_eventmsg = 1;
-       state->u.live.u.eventmsg = *code;
-       return ptrace_event(engine, tsk, PTRACE_EVENT_EXIT);
-}
-
-static int
-ptrace_unsafe_exec(struct utrace_attached_engine *engine,
-                  struct task_struct *tsk)
-{
-       struct ptrace_state *state = (struct ptrace_state *) engine->data;
-       int unsafe = LSM_UNSAFE_PTRACE;
-       if (state->u.live.cap_sys_ptrace)
-               unsafe = LSM_UNSAFE_PTRACE_CAP;
-       return unsafe;
-}
-
-static struct task_struct *
-ptrace_tracer_task(struct utrace_attached_engine *engine,
-                  struct task_struct *target)
-{
-       struct ptrace_state *state;
+       ret = -EPERM;
+       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT))
+               goto out_put_task_struct;
 
-       /*
-        * This call is not necessarily made by the target task,
-        * so ptrace might be getting detached while we run here.
-        * The state pointer will be NULL if that happens.
-        */
-       state = rcu_dereference((struct ptrace_state *) engine->data);
+       if (request == PTRACE_ATTACH) {
+               ret = ptrace_attach(child);
+               goto out_put_task_struct;
+       }
 
-       return state == NULL ? NULL : state->parent;
-}
+       ret = ptrace_check_attach(child, request == PTRACE_KILL);
+       if (ret < 0)
+               goto out_put_task_struct;
 
-static int
-ptrace_allow_access_process_vm(struct utrace_attached_engine *engine,
-                              struct task_struct *target,
-                              struct task_struct *caller)
-{
-       struct ptrace_state *state;
-       int ours;
+       ret = arch_ptrace(child, request, addr, data);
+       if (ret < 0)
+               goto out_put_task_struct;
 
-       /*
-        * This call is not necessarily made by the target task,
-        * so ptrace might be getting detached while we run here.
-        * The state pointer will be NULL if that happens.
-        */
-       rcu_read_lock();
-       state = rcu_dereference((struct ptrace_state *) engine->data);
-       ours = (state != NULL
-               && ((engine->flags & UTRACE_ACTION_QUIESCE)
-                   || (target->state == TASK_STOPPED))
-               && state->parent == caller);
-       rcu_read_unlock();
-
-       return ours && security_ptrace(caller, target) == 0;
+ out_put_task_struct:
+       put_task_struct(child);
+ out:
+       unlock_kernel();
+       return ret;
 }
-
-
-static const struct utrace_engine_ops ptrace_utrace_ops =
-{
-       .report_syscall_entry = ptrace_report_syscall_entry,
-       .report_syscall_exit = ptrace_report_syscall_exit,
-       .report_exec = ptrace_report_exec,
-       .report_jctl = ptrace_report_jctl,
-       .report_signal = ptrace_report_signal,
-       .report_vfork_done = ptrace_report_vfork_done,
-       .report_clone = ptrace_report_clone,
-       .report_exit = ptrace_report_exit,
-       .report_death = ptrace_report_death,
-       .report_reap = ptrace_report_reap,
-       .unsafe_exec = ptrace_unsafe_exec,
-       .tracer_task = ptrace_tracer_task,
-       .allow_access_process_vm = ptrace_allow_access_process_vm,
-};
-
-#endif
+#endif /* __ARCH_SYS_PTRACE */