DragonFlyBSD Kernel Audit
DF-0793 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/ufs/ufsmount.h b/sys/vfs/ufs/ufsmount.h
--- a/sys/vfs/ufs/ufsmount.h
+++ b/sys/vfs/ufs/ufsmount.h
@@ -86,6 +86,7 @@
 	int	um_i_effnlink_valid;		/* i_effnlink valid? */
 	struct inode **um_ihashtbl;		/* inum to inode map */
 	u_long	um_ihash;			/* size of hash table - 1 */
+	u_int	um_trim_pending;		/* in-flight async TRIM frees (DF-0793) */
 };
 
 /*
diff --git a/sys/vfs/ufs/ffs_alloc.c b/sys/vfs/ufs/ffs_alloc.c
--- a/sys/vfs/ufs/ffs_alloc.c
+++ b/sys/vfs/ufs/ffs_alloc.c
@@ -1627,6 +1627,7 @@
 	cdev_t i_dev; 
 	ino_t i_number;
 	uint32_t i_din_uid;
+	struct ufsmount *i_ump;		/* owning mount, for pending-TRIM drain */
 };
 
         
@@ -1638,6 +1639,8 @@
 	tp = ctx;
 	ffs_blkfree_cg(tp->i_fs, tp->i_devvp, tp->i_dev, tp->i_number,
 	    tp->i_din_uid, tp->bno, tp->size);
+	atomic_subtract_int(&tp->i_ump->um_trim_pending, 1);
+	wakeup(&tp->i_ump->um_trim_pending);
 	kfree(tp, M_TEMP);
 }
 
@@ -1667,7 +1670,7 @@
 void
 ffs_blkfree(struct inode *ip, ufs_daddr_t bno, long size) 
 {
-	struct mount *mp = ip->i_devvp->v_mount;
+	struct mount *mp = ip->i_vnode->v_mount;
 	struct ffs_blkfree_trim_params *tp;
 
 	if (!(mp->mnt_flag & MNT_TRIM)) {
@@ -1685,7 +1688,9 @@
 	tp->i_dev = ip->i_dev;
 	tp->i_din_uid = ip->i_uid;
 	tp->i_number = ip->i_number;
+	tp->i_ump = VFSTOUFS(mp);
 	tp->size = size;
+	atomic_add_int(&tp->i_ump->um_trim_pending, 1);
 
 	bp = getnewbuf(0, 0, 0, 1);
 	BUF_KERNPROC(bp);
diff --git a/sys/vfs/ufs/ffs_vfsops.c b/sys/vfs/ufs/ffs_vfsops.c
--- a/sys/vfs/ufs/ffs_vfsops.c
+++ b/sys/vfs/ufs/ffs_vfsops.c
@@ -860,6 +860,14 @@
 	vrele(ump->um_devvp);
 
 	ufs_ihashuninit(ump);
+	/*
+	 * Drain in-flight async TRIM frees before tearing down fs/ump/devvp.
+	 * ffs_blkfree_trim_task dereferences tp->i_fs / tp->i_devvp, which are
+	 * freed below; without this drain, a deferred task completing after
+	 * kfree() is a use-after-free (DF-0793).
+	 */
+	while (ump->um_trim_pending)
+		tsleep(&ump->um_trim_pending, 0, "ufstrim", hz);
 	kfree(fs->fs_csp, M_UFSMNT);
 	kfree(fs, M_UFSMNT);
 	kfree(ump, M_UFSMNT);