DragonFlyBSD Kernel Audit
DF-0946 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
--- a/sys/vm/swap_pager.c
+++ b/sys/vm/swap_pager.c
@@ -2379,10 +2379,24 @@
 	 *
 	 * NOTE: Decrement swb_count after the freeing operation (which
 	 *	 might block) to prevent racing destruction of the swblock.
+	 *
+	 * DF-0946: previously this was a `while' loop that re-read
+	 *	 swap->swb_pages[index] after the (potentially blocking)
+	 *	 call to swp_pager_freeswapspace().  swp_pager_freeswapspace()
+	 *	 acquires vm_token and may block in blist_free(); while
+	 *	 blocked, the lwkt object token is shed and a concurrent
+	 *	 meta_build() on another CPU can store a NEW swapblk into
+	 *	 the same slot.  On resume the first thread would re-read
+	 *	 the slot, see the new value, and free that swapblk out
+	 *	 from under the second caller.  Using `if' instead of
+	 *	 `while' frees only the value we observed before blocking
+	 *	 and leaves any value inserted by a concurrent meta_build()
+	 *	 intact.
 	 */
 	index &= SWAP_META_MASK;
 
-	while ((v = swap->swb_pages[index]) != SWAPBLK_NONE) {
+	v = swap->swb_pages[index];
+	if (v != SWAPBLK_NONE) {
 		swap->swb_pages[index] = SWAPBLK_NONE;
 		/* can block */
 		swp_pager_freeswapspace(object, v, 1);