[PATCH 2/3] fix utrace_control(DETACH) && utrace->death interaction

Oleg Nesterov oleg at redhat.com
Mon Aug 16 09:39:29 UTC 2010


Suppose that we want to detach the engine and free engine->data. To
avoid the races with our calbacks which can use ->data we should do
either

	err = utrace_set_events(0);
	if (err == ...)
		utrace_barrier();
	utrace_control(DETACH);

or

	err = utrace_control(DETACH);
	if (err = ...)
		utrace_barrier();

Neither variant works if we should care about report_quiesce() or
report_death(). Let's discuss the 2nd case, the 1st one have the
similar problems.

The problem is, utrace_control(DETACH) does nothing and returns
-EALREADY if utrace->death is set, this is not right. We can and
should detach in this case, we only should skip utrace_reset() to
avoid the race with utrace_report_death()->REPORT_CALLBACKS().

This was the main problem I hit during the testing. Consider the
exiting tracee with the valid engine->ops, suppose that
utrace_control(DETACH) is called right after utrace_report_death()
unlocks utrace->lock and before it does REPORT_CALLBACKS().

utrace_control() fails, but utrace_barrier() does not help after
that. It takes get_utrace_lock() successfully and returns 0.
The caller frees engine->data, but utrace_report_death() calls
->report_death() after that.

Kill utrace_control_dead(). Change utrace_control() to always
allow UTRACE_DETACH if engine has the valid ->ops. This means that
mark_engine_detached() can race with REPORT_CALLBACKS() but there
is nothing new. utrace_do_stop() is unnecessary and pointless in
this case, but it doesn't hurt.

Signed-off-by: Oleg Nesterov <oleg at redhat.com>
---

 kernel/utrace.c |   49 ++++++++++---------------------------------------
 1 file changed, 10 insertions(+), 39 deletions(-)

--- kstub/kernel/utrace.c~6_fix_control_dead	2010-08-16 11:17:05.000000000 +0200
+++ kstub/kernel/utrace.c	2010-08-16 11:17:51.000000000 +0200
@@ -928,37 +928,6 @@ void utrace_maybe_reap(struct task_struc
 	}
 }
 
-/*
- * You can't do anything to a dead task but detach it.
- * If release_task() has been called, you can't do that.
- *
- * On the exit path, DEATH and QUIESCE event bits are set only
- * before utrace_report_death() has taken the lock.  At that point,
- * the death report will come soon, so disallow detach until it's
- * done.  This prevents us from racing with it detaching itself.
- *
- * Called only when @target->exit_state is nonzero.
- */
-static inline int utrace_control_dead(struct task_struct *target,
-				      struct utrace *utrace,
-				      enum utrace_resume_action action)
-{
-	lockdep_assert_held(&utrace->lock);
-
-	if (action != UTRACE_DETACH || unlikely(utrace->reap))
-		return -ESRCH;
-
-	if (unlikely(utrace->death))
-		/*
-		 * We have already started the death report.  We can't
-		 * prevent the report_death and report_reap callbacks,
-		 * so tell the caller they will happen.
-		 */
-		return -EALREADY;
-
-	return 0;
-}
-
 /**
  * utrace_control - control a thread being traced by a tracing engine
  * @target:		thread to affect
@@ -1115,18 +1084,20 @@ int utrace_control(struct task_struct *t
 	ret = 0;
 
 	/*
-	 * ->exit_state can change under us, this doesn't matter.
-	 * We do not care about ->exit_state in fact, but we do
-	 * care about ->reap and ->death. If either flag is set,
-	 * we must also see ->exit_state != 0.
+	 * ->exit_state can change under us, this doesn't matter. But,
+	 * if utrace->death is set we must also see ->exit_state != 0,
+	 * this is what we care about.
 	 */
 	if (unlikely(target->exit_state)) {
-		ret = utrace_control_dead(target, utrace, action);
-		if (ret) {
+		/* You can't do anything to a dead task but detach it */
+		if (action != UTRACE_DETACH) {
 			spin_unlock(&utrace->lock);
-			return ret;
+			return -ESRCH;
 		}
-		reset = true;
+
+		/* If it is not set, we can safely do utrace_reset() */
+		if (likely(!utrace->death))
+			reset = true;
 	}
 
 	switch (action) {




More information about the utrace-devel mailing list