DF-0793 / fix.diff
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); |