git: 51b8ffb95c4f - main - fdesc_allocvp(): fix potential use after free

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 24 Mar 2023 17:47:41 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=51b8ffb95c4fe45f6825d551bd093889820a8115

commit 51b8ffb95c4fe45f6825d551bd093889820a8115
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2023-03-21 21:24:06 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2023-03-24 17:46:53 +0000

    fdesc_allocvp(): fix potential use after free
    
    Just owning the interlock is not enough for vget() to operate on the
    vnode race-free with vgone(), the vnode should be held.  Use
    vget_prep()/vget_finish() to avoid vholding the vnode explicitly, and
    drop LK_INTERLOCK.
    
    Reviewed by:    markj
    Tested by:      pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D39207
---
 sys/fs/fdescfs/fdesc_vnops.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c
index d3c7951672cf..afefaff8acf4 100644
--- a/sys/fs/fdescfs/fdesc_vnops.c
+++ b/sys/fs/fdescfs/fdesc_vnops.c
@@ -147,6 +147,7 @@ fdesc_allocvp(fdntype ftype, unsigned fd_fd, int ix, struct mount *mp,
 	struct fdhashhead *fc;
 	struct fdescnode *fd, *fd2;
 	struct vnode *vp, *vp2;
+	enum vgetstate vgs;
 	int error;
 
 	fc = FD_NHASH(ix);
@@ -166,9 +167,9 @@ loop:
 		if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
 			/* Get reference to vnode in case it's being free'd */
 			vp = fd->fd_vnode;
-			VI_LOCK(vp);
+			vgs = vget_prep(vp);
 			mtx_unlock(&fdesc_hashmtx);
-			if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
+			if (vget_finish(vp, LK_EXCLUSIVE, vgs) != 0)
 				goto loop;
 			*vpp = vp;
 			return (0);
@@ -218,9 +219,9 @@ loop:
 		if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) {
 			/* Get reference to vnode in case it's being free'd */
 			vp2 = fd2->fd_vnode;
-			VI_LOCK(vp2);
+			vgs = vget_prep(vp2);
 			mtx_unlock(&fdesc_hashmtx);
-			error = vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK);
+			error = vget_finish(vp2, LK_EXCLUSIVE, vgs);
 			/* Someone beat us, dec use count and wait for reclaim */
 			vgone(vp);
 			vput(vp);