DragonFlyBSD Kernel Audit
DF-3029 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/fuse/fuse_vnops.c
+++ b/sys/vfs/fuse/fuse_vnops.c
@@ -2011,8 +2011,22 @@
 	struct bio *bio;
 
 	while (fmp->dead == 0) {
-		tsleep(&fmp->helper_td, 0, "fuse_wio", 0);
+		/*
+		 * DF-3029: interlocked sleep so a wakeup() from
+		 * fuse_vop_strategy() between the queue-empty check and
+		 * tsleep() cannot be lost (a lost wakeup strands the bio
+		 * and blocks its waiter forever).
+		 */
+		tsleep_interlock(&fmp->helper_td, 0);
 		spin_lock(&fmp->helper_spin);
+		if (TAILQ_FIRST(&fmp->bioq) == NULL) {
+			spin_unlock(&fmp->helper_spin);
+			if (fmp->dead)
+				break;
+			tsleep(&fmp->helper_td, PINTERLOCKED,
+			       "fuse_wio", 0);
+			continue;
+		}
 		while ((bio = TAILQ_FIRST(&fmp->bioq)) != NULL) {
 			TAILQ_REMOVE(&fmp->bioq, bio, bio_act);
 			spin_unlock(&fmp->helper_spin);