DragonFlyBSD Kernel Audit
DF-0142 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/vfs_quota.c b/sys/kern/vfs_quota.c
--- a/sys/kern/vfs_quota.c
+++ b/sys/kern/vfs_quota.c
@@ -86,11 +86,24 @@
 {
 	struct ac_unode *unp, *res;
 
+	/*
+	 * The caller must NOT hold ac_spin: this M_WAITOK allocation is
+	 * allowed to sleep (the slab allocator may block in kmem_slab_alloc
+	 * via vm_map_lock / vm_wait).  Holding a spinlock across it panics
+	 * in lwkt_switch() (KASSERT gd->gd_spinlocks == 0).
+	 */
 	unp = kmalloc(sizeof(struct ac_unode), M_MOUNT, M_ZERO | M_WAITOK);
-
 	unp->left_bits = (uid >> ACCT_CHUNK_BITS);
+
+	spin_lock(&mp->mnt_acct.ac_spin);
 	res = RB_INSERT(ac_utree, &mp->mnt_acct.ac_uroot, unp);
-	KASSERT(res == NULL, ("unode_insert(): RB_INSERT didn't return NULL"));
+	if (res != NULL) {
+		/* raced with a concurrent inserter; keep the winner, drop ours */
+		spin_unlock(&mp->mnt_acct.ac_spin);
+		kfree(unp, M_MOUNT);
+		return res;
+	}
+	spin_unlock(&mp->mnt_acct.ac_spin);
 
 	return unp;
 }
@@ -100,11 +113,18 @@
 {
 	struct ac_gnode *gnp, *res;
 
+	/* See unode_insert(): caller must NOT hold ac_spin here. */
 	gnp = kmalloc(sizeof(struct ac_gnode), M_MOUNT, M_ZERO | M_WAITOK);
-
 	gnp->left_bits = (gid >> ACCT_CHUNK_BITS);
+
+	spin_lock(&mp->mnt_acct.ac_spin);
 	res = RB_INSERT(ac_gtree, &mp->mnt_acct.ac_groot, gnp);
-	KASSERT(res == NULL, ("gnode_insert(): RB_INSERT didn't return NULL"));
+	if (res != NULL) {
+		spin_unlock(&mp->mnt_acct.ac_spin);
+		kfree(gnp, M_MOUNT);
+		return res;
+	}
+	spin_unlock(&mp->mnt_acct.ac_spin);
 
 	return gnp;
 }
@@ -159,10 +179,16 @@
 
 	mp->mnt_acct.ac_bytes += delta;
 
-	if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL)
+	if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) {
+		spin_unlock(&mp->mnt_acct.ac_spin);
 		unp = unode_insert(mp, uid);
-	if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL)
+		spin_lock(&mp->mnt_acct.ac_spin);
+	}
+	if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) {
+		spin_unlock(&mp->mnt_acct.ac_spin);
 		gnp = gnode_insert(mp, gid);
+		spin_lock(&mp->mnt_acct.ac_spin);
+	}
 
 	/* update existing chunk */
 	unp->uid_chunk[(uid & ACCT_CHUNK_MASK)].space += delta;
@@ -250,14 +276,20 @@
 		if (prop_dictionary_get_uint32(item, "uid", &id)) {
 			ufind.left_bits = (id >> ACCT_CHUNK_BITS);
 			unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind);
-			if (unp == NULL)
+			if (unp == NULL) {
+				spin_unlock(&mp->mnt_acct.ac_spin);
 				unp = unode_insert(mp, id);
+				spin_lock(&mp->mnt_acct.ac_spin);
+			}
 			unp->uid_chunk[(id & ACCT_CHUNK_MASK)].space = space;
 		} else if (prop_dictionary_get_uint32(item, "gid", &id)) {
 			gfind.left_bits = (id >> ACCT_CHUNK_BITS);
 			gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind);
-			if (gnp == NULL)
+			if (gnp == NULL) {
+				spin_unlock(&mp->mnt_acct.ac_spin);
 				gnp = gnode_insert(mp, id);
+				spin_lock(&mp->mnt_acct.ac_spin);
+			}
 			gnp->gid_chunk[(id & ACCT_CHUNK_MASK)].space = space;
 		} else {
 			mp->mnt_acct.ac_bytes = space;
@@ -296,8 +328,11 @@
 	ufind.left_bits = (uid >> ACCT_CHUNK_BITS);
 
 	spin_lock(&mp->mnt_acct.ac_spin);
-	if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL)
+	if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) {
+		spin_unlock(&mp->mnt_acct.ac_spin);
 		unp = unode_insert(mp, uid);
+		spin_lock(&mp->mnt_acct.ac_spin);
+	}
 	unp->uid_chunk[(uid & ACCT_CHUNK_MASK)].limit = limit;
 	spin_unlock(&mp->mnt_acct.ac_spin);
 
@@ -317,8 +352,11 @@
 	gfind.left_bits = (gid >> ACCT_CHUNK_BITS);
 
 	spin_lock(&mp->mnt_acct.ac_spin);
-	if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL)
+	if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) {
+		spin_unlock(&mp->mnt_acct.ac_spin);
 		gnp = gnode_insert(mp, gid);
+		spin_lock(&mp->mnt_acct.ac_spin);
+	}
 	gnp->gid_chunk[(gid & ACCT_CHUNK_MASK)].limit = limit;
 	spin_unlock(&mp->mnt_acct.ac_spin);