The branch stable/14 has been updated by mjg:

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

commit b206bfb92c1f522c85ef1db44d42431e6c0d9334
Author:     Mateusz Guzik <m...@freebsd.org>
AuthorDate: 2023-09-23 02:04:06 +0000
Commit:     Mateusz Guzik <m...@freebsd.org>
CommitDate: 2023-10-09 23:19:00 +0000

    vfs cache: plug a hypothetical corner case when freeing
    
    cache_zap_unlocked_bucket is called with a bunch of addresses and
    without any locks held, forcing it to revalidate everything from
    scratch.
    
    It did not account for a case where the entry is reallocated with
    everything the same except for the target vnode.
    
    Should the target use a different lock than the one expected, freeing
    would proceed without being properly synchronized.
    
    Note this is almost impossible to happen in practice.
    
    (cherry picked from commit 0f15054f7990f9c772bea34778a8838aa05ebed8)
---
 sys/kern/vfs_cache.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index 38a39331e237..44295e12ee59 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -1645,6 +1645,7 @@ cache_zap_unlocked_bucket(struct namecache *ncp, struct 
componentname *cnp,
     struct mtx *blp)
 {
        struct namecache *rncp;
+       struct mtx *rvlp;
 
        cache_assert_bucket_unlocked(ncp);
 
@@ -1657,14 +1658,24 @@ cache_zap_unlocked_bucket(struct namecache *ncp, struct 
componentname *cnp,
                    !bcmp(rncp->nc_name, cnp->cn_nameptr, rncp->nc_nlen))
                        break;
        }
-       if (rncp != NULL) {
-               cache_zap_locked(rncp);
-               mtx_unlock(blp);
-               cache_unlock_vnodes(dvlp, vlp);
-               atomic_add_long(&zap_bucket_relock_success, 1);
-               return (0);
-       }
 
+       if (rncp == NULL)
+               goto out_mismatch;
+
+       if (!(ncp->nc_flag & NCF_NEGATIVE))
+               rvlp = VP2VNODELOCK(rncp->nc_vp);
+       else
+               rvlp = NULL;
+       if (rvlp != vlp)
+               goto out_mismatch;
+
+       cache_zap_locked(rncp);
+       mtx_unlock(blp);
+       cache_unlock_vnodes(dvlp, vlp);
+       atomic_add_long(&zap_bucket_relock_success, 1);
+       return (0);
+
+out_mismatch:
        mtx_unlock(blp);
        cache_unlock_vnodes(dvlp, vlp);
        return (EAGAIN);

Reply via email to