⬢ DragonFlyBSD Kernel Audit
DF-5839 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/lwkt_thread.c
+++ b/sys/kern/lwkt_thread.c
@@ -366,6 +366,25 @@
 	    KKASSERT((td->td_flags & (TDF_RUNNING|TDF_PREEMPT_LOCK|
 				      TDF_RUNQ)) == 0);
 	    gd->gd_freetd = NULL;
+	    /*
+	     * A foreign-cpu lwkt_hold() can land in the gd_freetd
+	     * publication window of lwkt_exit() (the per-cpu critical
+	     * section cannot exclude cross-cpu atomics), leaving a
+	     * published corpse with td_refs != 0.  Never rebirth such
+	     * a corpse: lwkt_init_thread()'s bzero would wipe the live
+	     * refcount and the stale lwkt_rele() would then decrement
+	     * the recycled thread's td_refs to -1 (permanent exit
+	     * livelock on stock kernels).  Dropping the corpse leaks
+	     * its cached kstack, which is safe: the holder still owns
+	     * the pointer.
+	     */
+	    if (td->td_refs) {
+		kprintf("lwkt_alloc_thread: freetd corpse %p still has "
+			"%d refs, dropping\n", td, td->td_refs);
+		td = objcache_get(thread_cache, M_WAITOK);
+		KKASSERT((td->td_flags & (TDF_RUNNING|TDF_PREEMPT_LOCK|
+					  TDF_RUNQ)) == 0);
+	    }
 	} else {
 	    td = objcache_get(thread_cache, M_WAITOK);
 	    KKASSERT((td->td_flags & (TDF_RUNNING|TDF_PREEMPT_LOCK|