From 0745d837c2e9e37bd069bd7c05c8f9892fba0752 Mon Sep 17 00:00:00 2001 From: "Jason A. Harmening" Date: Sat, 25 Mar 2023 20:41:33 -0500 Subject: [PATCH] unionfs: prevent upperrootvp from being recycled during mount If upperrootvp is doomed by a concurrent unmount, unionfs_nodeget() may return without a reference or lock on it. unionfs_domount() must prevent the vnode from being recycled for use by a different file until it is finished with the vnode, namely once vfs_register_upper_from_vp() fails. Accomplish this by holding the reference returned by namei() a bit longer. Reviewed by: kib, markj Tested by: pho Differential Revision: https://reviews.freebsd.org/D39767 --- sys/fs/unionfs/union_vfsops.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c index 8831397c8c5b..cd6c7bd90655 100644 --- a/sys/fs/unionfs/union_vfsops.c +++ b/sys/fs/unionfs/union_vfsops.c @@ -285,8 +285,8 @@ unionfs_domount(struct mount *mp) */ error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, NULLVP, &(ump->um_rootvp), NULL); - vrele(upperrootvp); if (error != 0) { + vrele(upperrootvp); free(ump, M_UNIONFSMNT); mp->mnt_data = NULL; return (error); @@ -295,11 +295,24 @@ unionfs_domount(struct mount *mp) KASSERT((ump->um_rootvp->v_vflag & VV_ROOT) != 0, ("%s: rootvp without VV_ROOT", __func__)); + /* + * Do not release the namei() reference on upperrootvp until after + * we attempt to register the upper mounts. A concurrent unmount + * of the upper or lower FS may have caused unionfs_nodeget() to + * create a unionfs node with a NULL upper or lower vp and with + * no reference held on upperrootvp or lowerrootvp. + * vfs_register_upper() should subsequently fail, which is what + * we want, but we must ensure neither underlying vnode can be + * reused until that happens. We assume the caller holds a reference + * to lowerrootvp as it is the mount's covered vnode. + */ lowermp = vfs_register_upper_from_vp(ump->um_lowervp, mp, &ump->um_lower_link); uppermp = vfs_register_upper_from_vp(ump->um_uppervp, mp, &ump->um_upper_link); + vrele(upperrootvp); + if (lowermp == NULL || uppermp == NULL) { if (lowermp != NULL) vfs_unregister_upper(lowermp, &ump->um_lower_link);