DragonFlyBSD Kernel Audit
DF-2990 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/ufs/ffs_softdep.c
+++ b/sys/vfs/ufs/ffs_softdep.c
@@ -1007,22 +1007,37 @@
 	struct newblk *newblk;
 	struct newblk_hashhead *newblkhd;
 
+	/*
+	 * The newblk hash chains are shared with the removal sites in
+	 * softdep_setup_allocdirect() and setup_allocindir_phase2(), which
+	 * run under the softdep lock.  Take the lock here as well so that
+	 * lock-free inserts/lookups cannot interleave with locked removes
+	 * on the same chain (the pagedep and inodedep lookups already
+	 * follow this discipline).
+	 */
+	ACQUIRE_LOCK(&lk);
 	newblkhd = NEWBLK_HASH(fs, newblkno);
 top:
 	*newblkpp = newblk_find(newblkhd, fs, newblkno);
-	if (*newblkpp)
+	if (*newblkpp) {
+		FREE_LOCK(&lk);
 		return(1);
-	if ((flags & DEPALLOC) == 0)
+	}
+	if ((flags & DEPALLOC) == 0) {
+		FREE_LOCK(&lk);
 		return (0);
-	if (sema_get(&newblk_in_progress, NULL) == 0)
+	}
+	if (sema_get(&newblk_in_progress, &lk) == 0)
 		goto top;
 
+	FREE_LOCK(&lk);
 	newblk = kmalloc(sizeof(struct newblk), M_NEWBLK,
 			 M_SOFTDEP_FLAGS | M_ZERO);
+	ACQUIRE_LOCK(&lk);
 
 	if (newblk_find(newblkhd, fs, newblkno)) {
 		kprintf("newblk_lookup: blocking race avoided\n");
-		sema_release(&pagedep_in_progress, NULL);
+		sema_release(&newblk_in_progress, &lk);
 		kfree(newblk, M_NEWBLK);
 		goto top;
 	}
@@ -1030,8 +1045,9 @@
 	newblk->nb_fs = fs;
 	newblk->nb_newblkno = newblkno;
 	LIST_INSERT_HEAD(newblkhd, newblk, nb_hash);
-	sema_release(&newblk_in_progress, NULL);
+	sema_release(&newblk_in_progress, &lk);
 	*newblkpp = newblk;
+	FREE_LOCK(&lk);
 	return (0);
 }