[PATCH 07] rework prepare_ptrace_attach/finish_ptrace_attach

Oleg Nesterov oleg at redhat.com
Mon Aug 17 15:22:58 UTC 2009


Introduce ptrace_attach_task() and ptrace_abort_attach(). There are
simplified versions of prepare_ptrace_attach/finish_ptrace_attach and
should be used instead. Change ptrace_attach() accordingly.

This patch doesn't kill the old helpers and doesn't change ptrace_traceme()
because I noticed it has another bug.

Note the "XXX" comment in ptrace_abort_attach(). I am not sure a simple
UTRACE_DETACH is always correct. We already did utrace_set_events(), we
must not, say, lose a signal if it comes in between.

---

 kernel/ptrace.c |   57 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 49 insertions(+), 8 deletions(-)

--- PU/kernel/ptrace.c~07_ATTACH	2009-08-13 18:41:46.000000000 +0200
+++ PU/kernel/ptrace.c	2009-08-17 15:31:07.000000000 +0200
@@ -509,6 +509,46 @@ static struct utrace_engine *prepare_ptr
 	return engine;
 }
 
+/*
+ * Attach a utrace engine for ptrace and set up its event mask.
+ * Returns error code or 0 on success.
+ */
+static int ptrace_attach_task(struct task_struct *tracee)
+{
+	struct utrace_engine *engine;
+	unsigned long events;
+
+	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;
+	}
+	/*
+	 * We need QUIESCE for resume handling, CLONE to check
+	 * for CLONE_PTRACE, other events are always reported.
+	 */
+	events = UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
+		 UTRACE_EVENT(EXEC) | UTRACE_EVENT_SIGNAL_ALL;
+	/*
+	 * It can fail only if the tracee is dead, the caller
+	 * must notice this before setting PT_PTRACED.
+	 */
+	utrace_set_events(tracee, engine, events);
+	utrace_engine_put(engine);
+	return 0;
+}
+
+static void ptrace_abort_attach(struct task_struct *tracee)
+{
+	/* XXX we raced with detach. Double check UTRACE_DETACH is enough */
+	ptrace_detach_task(tracee, 0);
+}
+
 int ptrace_check_attach(struct task_struct *child, int kill)
 {
 	struct utrace_engine *engine;
@@ -587,19 +627,14 @@ bool ptrace_may_access(struct task_struc
 int ptrace_attach(struct task_struct *task)
 {
 	int retval;
-	struct utrace_engine *engine;
 
 	audit_ptrace(task);
 
 	retval = -EPERM;
 	if (unlikely(task->flags & PF_KTHREAD))
-		return retval;
+		goto out;
 	if (same_thread_group(task, current))
-		return retval;
-
-	engine = prepare_ptrace_attach(task, current);
-	if (unlikely(IS_ERR(engine)))
-		return PTR_ERR(engine);
+		goto out;
 
 	/*
 	 * Protect exec's credential calculations against our interference;
@@ -616,6 +651,10 @@ int ptrace_attach(struct task_struct *ta
 	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))
@@ -633,10 +672,12 @@ int ptrace_attach(struct task_struct *ta
 	retval = 0;
 unlock_tasklist:
 	write_unlock_irq(&tasklist_lock);
+	if (retval)
+		ptrace_abort_attach(task);
 unlock_creds:
 	mutex_unlock(&task->cred_guard_mutex);
 out:
-	return finish_ptrace_attach(task, engine, retval);
+	return retval;
 }
 
 /**




More information about the utrace-devel mailing list