⬢ DragonFlyBSD Kernel Audit
DF-5799 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer2/hammer2_ondisk.c b/sys/vfs/hammer2/hammer2_ondisk.c
--- a/sys/vfs/hammer2/hammer2_ondisk.c
+++ b/sys/vfs/hammer2/hammer2_ondisk.c
@@ -222,8 +222,14 @@
 		TAILQ_REMOVE(devvpl, e, entry);
 		/* devvp */
 		KKASSERT(e->devvp);
-		if (e->devvp->v_rdev)
-			e->devvp->v_rdev->si_mountpoint = NULL;
+		/*
+		 * NOTE: Do not clear v_rdev->si_mountpoint here.  This list
+		 * may reference devices owned by a still-live hammer2 mount
+		 * or by another filesystem entirely (see the secondary-mount
+		 * and failed-mount paths in hammer2_vfsops.c).  The marker
+		 * lifecycle is managed by hammer2_devvp_mount() /
+		 * hammer2_devvp_unmount() instead.
+		 */
 		vrele(e->devvp);
 		e->devvp = NULL;
 		/* path */
@@ -234,6 +240,59 @@
 	}
 }
 
+/*
+ * Set the mountpoint marker on all devices belonging to hmp.  Called when
+ * a mount is established on the device(s), repointing the marker at the
+ * most recent mount so it never references a freed struct mount.
+ */
+void
+hammer2_devvp_mount(hammer2_dev_t *hmp, struct mount *mp)
+{
+	hammer2_devvp_t *e;
+
+	TAILQ_FOREACH(e, &hmp->devvpl, entry) {
+		if (e->devvp && e->devvp->v_rdev)
+			e->devvp->v_rdev->si_mountpoint = mp;
+	}
+}
+
+/*
+ * Clear the mountpoint marker on all devices belonging to hmp, but only
+ * where it still points at the specified mount (ownership check, same as
+ * hammer1's hammer_unload_volume()).  This prevents cleanup code running
+ * against devices owned by other filesystems, or against devices backing
+ * a still-live hammer2 mount, from stripping their protection.
+ */
+void
+hammer2_devvp_unmount(hammer2_dev_t *hmp, struct mount *mp)
+{
+	hammer2_devvp_t *e;
+
+	TAILQ_FOREACH(e, &hmp->devvpl, entry) {
+		if (e->devvp && e->devvp->v_rdev &&
+		    e->devvp->v_rdev->si_mountpoint == mp)
+			e->devvp->v_rdev->si_mountpoint = NULL;
+	}
+}
+
+/*
+ * Unconditionally clear the mountpoint marker on all devices belonging to
+ * hmp.  Only used at final device teardown (mount_count == 0), where no
+ * hammer2 mount remains and any marker left on these devices was placed
+ * there by hammer2 (possibly stale from an earlier mount).
+ */
+void
+hammer2_devvp_unmount_all(hammer2_dev_t *hmp)
+{
+	hammer2_devvp_t *e;
+
+	TAILQ_FOREACH(e, &hmp->devvpl, entry) {
+		if (e->devvp && e->devvp->v_rdev &&
+		    e->devvp->v_rdev->si_mountpoint != NULL)
+			e->devvp->v_rdev->si_mountpoint = NULL;
+	}
+}
+
 static int
 hammer2_verify_volumes_common(const hammer2_volume_t *volumes,
 			      const hammer2_volume_data_t *rootvoldata)
@@ -702,6 +761,17 @@
 		if (!error)
 			error = hammer2_verify_volumes(volumes, rootvoldata);
 	}
+	if (error) {
+		/*
+		 * On failure clear only the markers we placed ourselves;
+		 * hammer2_cleanup_devvp() no longer touches si_mountpoint.
+		 */
+		TAILQ_FOREACH(e, devvpl, entry) {
+			if (e->devvp && e->devvp->v_rdev &&
+			    e->devvp->v_rdev->si_mountpoint == mp)
+				e->devvp->v_rdev->si_mountpoint = NULL;
+		}
+	}
 	kfree(voldata, M_HAMMER2);
 
 	return error;
diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h
--- a/sys/vfs/hammer2/hammer2.h
+++ b/sys/vfs/hammer2/hammer2.h
@@ -1954,6 +1954,9 @@
 			hammer2_volume_data_t *rootvoldata,
 			int *rootvolzone,
 			struct vnode **rootvoldevvp);
+void hammer2_devvp_mount(hammer2_dev_t *hmp, struct mount *mp);
+void hammer2_devvp_unmount(hammer2_dev_t *hmp, struct mount *mp);
+void hammer2_devvp_unmount_all(hammer2_dev_t *hmp);
 hammer2_volume_t *hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset);
 
 /*
diff --git a/sys/vfs/hammer2/hammer2_vfsops.c b/sys/vfs/hammer2/hammer2_vfsops.c
--- a/sys/vfs/hammer2/hammer2_vfsops.c
+++ b/sys/vfs/hammer2/hammer2_vfsops.c
@@ -502,8 +502,10 @@
 		 * If the PFS is already mounted we must account
 		 * for the mount_count here.
 		 */
-		if (pmp->mp)
+		if (pmp->mp) {
 			++chain->hmp->mount_count;
+			hammer2_devvp_mount(chain->hmp, pmp->mp);
+		}
 
 		/*
 		 * May have to fixup dirty chain tracking.  Previous
@@ -1700,6 +1702,11 @@
 		if (rchain == NULL)
 			continue;
 		++rchain->hmp->mount_count;
+		/*
+		 * (Re)own the device marker for this mount, keeping it
+		 * pointed at a live struct mount.
+		 */
+		hammer2_devvp_mount(rchain->hmp, mp);
 	}
 
 	/*
@@ -1753,6 +1760,39 @@
 				continue;
 			--rchain->hmp->mount_count;
 			/* scrapping hmp now may invalidate the pmp */
+			if (rchain->hmp->mount_count) {
+				/*
+				 * Repoint the device marker at a mount that
+				 * is still live so it never goes stale.
+				 */
+				hammer2_pfs_t *pmp2;
+				int j;
+				int found = 0;
+
+				TAILQ_FOREACH(pmp2, &hammer2_pfslist,
+					      mntentry) {
+					if (pmp2->mp == NULL)
+						continue;
+					for (j = 0; j < HAMMER2_MAXCLUSTER;
+					     ++j) {
+						if (pmp2->pfs_hmps[j] ==
+						    rchain->hmp) {
+							hammer2_devvp_mount(
+								rchain->hmp,
+								pmp2->mp);
+							found = 1;
+							break;
+						}
+					}
+					if (found)
+						break;
+				}
+				if (!found)
+					hammer2_devvp_unmount(rchain->hmp, mp);
+			} else {
+				/* last mount on this device went away */
+				hammer2_devvp_unmount(rchain->hmp, mp);
+			}
 		}
 again:
 		TAILQ_FOREACH(hmp, &hammer2_mntlist, mntentry) {
@@ -1830,6 +1870,14 @@
 	KKASSERT(hmp->spmp == NULL);
 
 	/*
+	 * Final device teardown: no hammer2 mounts remain on these devices.
+	 * Drop any (possibly stale) marker we placed on them.  Markers owned
+	 * by other filesystems cannot exist here because vfs_mountedon()
+	 * would have blocked their mounts while ours was established.
+	 */
+	hammer2_devvp_unmount_all(hmp);
+
+	/*
 	 * Finish up with the device vnode
 	 */
 	if (!TAILQ_EMPTY(&hmp->devvpl)) {