Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Mar 2023 13:19:35 GMT
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 0f5b6f9a041e - main - fdescfs: Fix a file ref leak
Message-ID:  <202303221319.32MDJZ7T097041@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=0f5b6f9a041e9cca3b376f6ec909374938887a3b

commit 0f5b6f9a041e9cca3b376f6ec909374938887a3b
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-03-22 12:52:57 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-03-22 13:19:27 +0000

    fdescfs: Fix a file ref leak
    
    In fdesc_lookup(), vn_vget_ino_gen() may fail without invoking the
    callback, in which case the ref on fp is leaked.  This happens if the
    fdescfs mount is being concurrently unmounted.  Moreover, we cannot
    safely drop the ref while the dvp is locked.
    
    So:
    - Use a flag variable to indicate whether the ref is dropped.
    - Reorganize things to handle the leak.
    
    Reported by:    C Turt <ecturt@gmail.com>
    Reviewed by:    mjg, kib
    Tested by:      pho
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D39189
---
 sys/fs/fdescfs/fdesc_vnops.c | 42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c
index 0949dcc7eb29..d3c7951672cf 100644
--- a/sys/fs/fdescfs/fdesc_vnops.c
+++ b/sys/fs/fdescfs/fdesc_vnops.c
@@ -246,6 +246,7 @@ struct fdesc_get_ino_args {
 	int ix;
 	struct file *fp;
 	struct thread *td;
+	bool fdropped;
 };
 
 static int
@@ -268,6 +269,7 @@ fdesc_get_ino_alloc(struct mount *mp, void *arg, int lkflags,
 		error = fdesc_allocvp(a->ftype, a->fd_fd, a->ix, mp, rvp);
 	}
 	fdrop(a->fp, a->td);
+	a->fdropped = true;
 	return (error);
 }
 
@@ -288,6 +290,7 @@ fdesc_lookup(struct vop_lookup_args *ap)
 	int nlen = cnp->cn_namelen;
 	u_int fd, fd1;
 	int error;
+	bool fdropped;
 	struct vnode *fvp;
 
 	if ((cnp->cn_flags & ISLASTCN) &&
@@ -331,24 +334,10 @@ fdesc_lookup(struct vop_lookup_args *ap)
 	 */
 	if ((error = fget(td, fd, &cap_no_rights, &fp)) != 0)
 		goto bad;
+	fdropped = false;
 
-	/* Check if we're looking up ourselves. */
-	if (VTOFDESC(dvp)->fd_ix == FD_DESC + fd) {
-		/*
-		 * In case we're holding the last reference to the file, the dvp
-		 * will be re-acquired.
-		 */
-		vhold(dvp);
-		VOP_UNLOCK(dvp);
-		fdrop(fp, td);
-
-		/* Re-aquire the lock afterwards. */
-		vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
-		vdrop(dvp);
-		fvp = dvp;
-		if (VN_IS_DOOMED(dvp))
-			error = ENOENT;
-	} else {
+	/* Make sure we're not looking up the dvp itself. */
+	if (VTOFDESC(dvp)->fd_ix != FD_DESC + fd) {
 		/*
 		 * Unlock our root node (dvp) when doing this, since we might
 		 * deadlock since the vnode might be locked by another thread
@@ -362,8 +351,27 @@ fdesc_lookup(struct vop_lookup_args *ap)
 		arg.ix = FD_DESC + fd;
 		arg.fp = fp;
 		arg.td = td;
+		arg.fdropped = fdropped;
 		error = vn_vget_ino_gen(dvp, fdesc_get_ino_alloc, &arg,
 		    LK_EXCLUSIVE, &fvp);
+		fdropped = arg.fdropped;
+	}
+
+	if (!fdropped) {
+		/*
+		 * In case we're holding the last reference to the file, the dvp
+		 * will be re-acquired.
+		 */
+		vhold(dvp);
+		VOP_UNLOCK(dvp);
+		fdrop(fp, td);
+		fdropped = true;
+
+		vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
+		vdrop(dvp);
+		fvp = dvp;
+		if (error == 0 && VN_IS_DOOMED(dvp))
+			error = ENOENT;
 	}
 
 	if (error)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202303221319.32MDJZ7T097041>