git: 586fed0b0356 - main - vfs_lookup_cross_mount(): restore previous do...while loop

From: Jason A. Harmening <jah_at_FreeBSD.org>
Date: Sat, 04 Nov 2023 17:15:50 UTC
The branch main has been updated by jah:

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

commit 586fed0b03561558644eccc37f824c7110500182
Author:     Jason A. Harmening <jah@FreeBSD.org>
AuthorDate: 2023-11-04 16:56:45 +0000
Commit:     Jason A. Harmening <jah@FreeBSD.org>
CommitDate: 2023-11-04 17:15:20 +0000

    vfs_lookup_cross_mount(): restore previous do...while loop
    
    When the cross-mount walking logic in vfs_lookup() was factored into
    a separate function, the main cross-mount traversal loop was changed
    from a do...while loop conditional on the current vnode having
    VIRF_MOUNTPOINT set to an unconditional for(;;) loop.  For the
    unionfs 'crosslock' case in which the vnode may be re-locked, this
    meant that continuing the loop upon finding inconsistent
    v_mountedhere state would no longer branch to a check that the vnode
    is in fact still a mountpoint.  This would in turn lead to over-
    iteration and, for INVARIANTS builds, a failed assert on the next
    iteration.
    
    Fix this by restoring the previous loop behavior.
    
    Reported by:    pho
    Tested by:      pho
    Fixes:          80bd5ef0702562c546fa1717e8fe221058974eac
    MFC after:      1 week
---
 sys/kern/vfs_lookup.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index d75351c34314..922adda33b94 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -882,7 +882,7 @@ vfs_lookup_cross_mount(struct nameidata *ndp)
 	 * The vnode has been mounted on, find the root of the mounted
 	 * filesystem.
 	 */
-	for (;;) {
+	do {
 		mp = dp->v_mountedhere;
 		ASSERT_VOP_LOCKED(dp, __func__);
 		VNPASS((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0 && mp != NULL, dp);
@@ -915,6 +915,12 @@ vfs_lookup_cross_mount(struct nameidata *ndp)
 					break;
 				}
 				if (dp->v_mountedhere != mp) {
+					/*
+					 * Note that we rely on the
+					 * VIRF_MOUNTPOINT loop condition to
+					 * ensure we stop iterating if dp is
+					 * no longer a mountpoint at all.
+					 */
 					continue;
 				}
 			} else
@@ -939,9 +945,7 @@ vfs_lookup_cross_mount(struct nameidata *ndp)
 		if (error != 0)
 			break;
 		ndp->ni_vp = dp = tdp;
-		if ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) == 0)
-			break;
-	}
+	} while ((vn_irflag_read(dp) & VIRF_MOUNTPOINT) != 0);
 
 	return (error);
 }