git: aec97963cd03 - main - msdosfs: do no allow lookup to return vdp except for dot lookups

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 08 Jan 2022 04:29:33 UTC
The branch main has been updated by kib:

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

commit aec97963cd03f10e04083537ed449a84a5e42f87
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-12-26 21:51:48 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-01-08 03:41:44 +0000

    msdosfs: do no allow lookup to return vdp except for dot lookups
    
    In collaboaration with: pho
    Reviewed by:    markj, mckusick
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D33721
---
 sys/fs/msdosfs/msdosfs_lookup.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c
index 38bded459692..3db9665f6094 100644
--- a/sys/fs/msdosfs/msdosfs_lookup.c
+++ b/sys/fs/msdosfs/msdosfs_lookup.c
@@ -63,6 +63,28 @@
 #include <fs/msdosfs/fat.h>
 #include <fs/msdosfs/msdosfsmount.h>
 
+static int
+msdosfs_lookup_checker(struct msdosfsmount *pmp, struct vnode *dvp,
+    struct denode *tdp, struct vnode **vpp)
+{
+	struct vnode *vp;
+
+	vp = DETOV(tdp);
+
+	/*
+	 * Lookup assumes that directory cannot be hardlinked.
+	 * Corrupted msdosfs filesystem could break this assumption.
+	 */
+	if (vp == dvp) {
+		vput(vp);
+		*vpp = NULL;
+		return (EBADF);
+	}
+
+	*vpp = vp;
+	return (0);
+}
+
 int
 msdosfs_lookup(struct vop_cachedlookup_args *ap)
 {
@@ -501,8 +523,7 @@ foundroot:
 		error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE, &tdp);
 		if (error)
 			return (error);
-		*vpp = DETOV(tdp);
-		return (0);
+		return (msdosfs_lookup_checker(pmp, vdp, tdp, vpp));
 	}
 
 	/*
@@ -529,7 +550,9 @@ foundroot:
 		if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
 		    &tdp)) != 0)
 			return (error);
-		*vpp = DETOV(tdp);
+		if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp))
+		    != 0)
+			return (error);
 		cnp->cn_flags |= SAVENAME;
 		return (0);
 	}
@@ -572,6 +595,7 @@ foundroot:
 			vput(*vpp);
 			goto restart;
 		}
+		return (msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp));
 	} else if (dp->de_StartCluster == scn && isadir) {
 		if (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.') {
 			/* fs is corrupted, non-dot lookup returned dvp */
@@ -583,7 +607,8 @@ foundroot:
 		if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,
 		    &tdp)) != 0)
 			return (error);
-		*vpp = DETOV(tdp);
+		if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp)) != 0)
+			return (error);
 	}
 
 	/*