We think we tracked down a defect in timeout/untimeout in
FreeBSD.

We have reduced the problem to the following scenario:

2+ cpu system, one cpu is running softclock at the same time
another thread is running on another cpu which makes use of
timeout/untimeout.

CPU 0 is running "softclock"
CPU 1 is running "driver" with Giant held.

softclock: mtx_lock_spin(&callout_lock)
softclock: CACHES the callout structure's fields.
softclock: sees that it's a CALLOUT_LOCAL_ALLOC
softclock: executes this code:
  if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
        c->c_func = NULL;
        c->c_flags = CALLOUT_LOCAL_ALLOC;
        SLIST_INSERT_HEAD(&callfree, c,
                c_links.sle);
        curr_callout = NULL;
  } else {
  
  NOTE: that c->c_func has been set to NULL and curr_callout
        is also NULL.
softclock: mtx_unlock_spin(&callout_lock)
driver: calls untimeout(), the following sequence happens:
        mtx_lock_spin(&callout_lock);
        if (handle.callout->c_func == ftn && handle.callout->c_arg == arg)
                callout_stop(handle.callout);
        mtx_unlock_spin(&callout_lock);

  NOTE: untimeout() sees that handle.callout->c_func is not set
        to the function so it does NOT call callout_stop(9)!
driver: free's backing structure for c->c_arg.
softclock: executes callout.
softclock: likely crashes at this point due to access after free.

I have a patch I'm trying out here, but I need feedback on it.

The way the patch works is to treat CALLOUT_LOCAL_ALLOC (timeout/untimeout)
callouts the same as ~CALLOUT_LOCAL_ALLOC allocs, and moves the
freelist manipulation to the end of the callout dispatch.

Some light testing seems to have the system work.

We are doing some testing in-house to also make sure this works.

Please provide feedback.

See attached delta.

-- 
- Alfred Perlstein
Index: kern_timeout.c
===================================================================
RCS file: /cvs/ncvs/src/sys/kern/kern_timeout.c,v
retrieving revision 1.97.2.2
diff -u -r1.97.2.2 kern_timeout.c
--- kern_timeout.c	26 Sep 2005 19:49:12 -0000	1.97.2.2
+++ kern_timeout.c	15 Mar 2008 02:28:48 -0000
@@ -241,17 +241,8 @@
 				c_arg = c->c_arg;
 				c_mtx = c->c_mtx;
 				c_flags = c->c_flags;
-				if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
-					c->c_func = NULL;
-					c->c_flags = CALLOUT_LOCAL_ALLOC;
-					SLIST_INSERT_HEAD(&callfree, c,
-							  c_links.sle);
-					curr_callout = NULL;
-				} else {
-					c->c_flags =
-					    (c->c_flags & ~CALLOUT_PENDING);
-					curr_callout = c;
-				}
+				c->c_flags &= ~CALLOUT_PENDING;
+				curr_callout = c;
 				curr_cancelled = 0;
 				mtx_unlock_spin(&callout_lock);
 				if (c_mtx != NULL) {
@@ -310,6 +301,12 @@
 					mtx_unlock(c_mtx);
 				mtx_lock_spin(&callout_lock);
 done_locked:
+				if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
+					c->c_func = NULL;
+					c->c_flags = CALLOUT_LOCAL_ALLOC;
+					SLIST_INSERT_HEAD(&callfree, c,
+							  c_links.sle);
+				}
 				curr_callout = NULL;
 				if (wakeup_needed) {
 					/*
_______________________________________________
freebsd-stable@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-stable
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to