[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