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