DragonFlyBSD Kernel Audit
DF-0925 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/fuse/fuse.h b/sys/vfs/fuse/fuse.h
--- a/sys/vfs/fuse/fuse.h
+++ b/sys/vfs/fuse/fuse.h
@@ -116,6 +116,7 @@
 	uint64_t ino;
 	enum vtype type;
 	size_t size;
+	uint32_t fn_refcnt;	/* prevents free during concurrent lookup */
 	uint64_t nlookup;
 	uint64_t fh;
 	bool closed; /* XXX associated with closed fh */

diff --git a/sys/vfs/fuse/fuse_node.c b/sys/vfs/fuse/fuse_node.c
--- a/sys/vfs/fuse/fuse_node.c
+++ b/sys/vfs/fuse/fuse_node.c
@@ -62,6 +62,7 @@
 	mtx_init(&fnp->node_lock, "fuse_node_lock");
 
 	fnp->ino = ino;
+	fnp->fn_refcnt = 1;	/* tree's reference */
 	fnp->type = vtyp;
 	fnp->size = 0;
 	fnp->nlookup = 0;
@@ -80,11 +81,13 @@
 {
 	fuse_dbg("free ino=%ju\n", fnp->ino);
 
+	KKASSERT(fnp->fn_refcnt > 0);
 	mtx_lock(&fmp->ino_lock);
 	RB_REMOVE(fuse_node_tree, &fmp->node_head, fnp);
 	mtx_unlock(&fmp->ino_lock);
 
-	objcache_put(fuse_node_objcache, fnp);
+	if (atomic_fetchadd_int(&fnp->fn_refcnt, -1) == 1)
+		objcache_put(fuse_node_objcache, fnp);
 }
 
 /*
@@ -108,10 +111,18 @@
 	if (fnp == NULL) {
 		fuse_node_new(fmp, ino, vtyp, &fnp);
 		allocated = 1;
+	} else {
+		atomic_add_int(&fnp->fn_refcnt, 1);	/* pin while we use it */
 	}
 	mtx_unlock(&fmp->ino_lock);
 
 	error = fuse_node_vn(fnp, vpp);
+
+	if (!allocated) {
+		if (atomic_fetchadd_int(&fnp->fn_refcnt, -1) == 1)
+			objcache_put(fuse_node_objcache, fnp);
+	}
+
 	if (error) {
 		if (allocated)
 			fuse_node_free(fmp, fnp);

diff --git a/sys/vfs/fuse/fuse_vnops.c b/sys/vfs/fuse/fuse_vnops.c
--- a/sys/vfs/fuse/fuse_vnops.c
+++ b/sys/vfs/fuse/fuse_vnops.c
@@ -1791,7 +1791,9 @@
 
 	if (fnp) {
 		vp->v_data = NULL;
+		mtx_lock(&fnp->node_lock);
 		fnp->vp = NULL;
+		mtx_unlock(&fnp->node_lock);
 		fuse_dbg("ino=%ju\n", fnp->ino);
 
 		if (fnp != fmp->rfnp)