[PATCH 2] utrace-ptrace-mini: introduce the empty ptrace_utrace_ops
Oleg Nesterov
oleg at redhat.com
Tue Aug 25 16:52:44 UTC 2009
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);
}
}
More information about the utrace-devel
mailing list