--- a/sys/kern/vfs_mount.c 2026-09-01 02:05:26.425046951 +0000 +++ b/sys/kern/vfs_mount.c 2026-09-01 02:07:28.231508072 +0000 @@ -138,6 +138,7 @@ static struct lwkt_token mntid_token; static struct mount dummymount; +static volatile uint64_t mnt_cookie_gen; /* note: mountlist exported to pstat */ struct mntlist mountlist = TAILQ_HEAD_INITIALIZER(mountlist); @@ -380,6 +381,7 @@ TAILQ_INIT(&mp->mnt_nvnodelist); TAILQ_INIT(&mp->mnt_reservedvnlist); TAILQ_INIT(&mp->mnt_jlist); + mp->mnt_cookie = atomic_fetchadd_64(&mnt_cookie_gen, 1) + 1; mp->mnt_nvnodelistsize = 0; mp->mnt_flag = 0; mp->mnt_hold = 1; /* hold for umount last drop */ @@ -708,6 +710,39 @@ } /* + * mountlist_hold (MP SAFE) + * + * Like mountlist_exists(), but if the mount is still on the mountlist + * a hold is acquired so the caller can safely use the returned mount. + * mountlist_exists() only compares pointers and gives the caller no + * lifetime guarantee whatsoever (a mount that is concurrently being + * unmounted can be ripped off the list and freed right after the + * check, and a mount reusing the freed structure's address makes the + * check succeed against the wrong mount). + * + * Returns the held mount (caller must mount_drop()), or NULL if the + * mount is no longer on the mountlist. + */ +struct mount * +mountlist_hold(struct mount *mp) +{ + struct mount *found = NULL; + struct mount *lmp; + + lwkt_gettoken(&mountlist_token); + TAILQ_FOREACH(lmp, &mountlist, mnt_list) { + if (lmp == mp) { + mount_hold(lmp); + found = lmp; + break; + } + } + lwkt_reltoken(&mountlist_token); + + return (found); +} + +/* * mountlist_scan * * Safely scan the mount points on the mount list. Each mountpoint --- a/sys/kern/vfs_quota.c 2026-09-01 02:05:26.425046951 +0000 +++ b/sys/kern/vfs_quota.c 2026-09-01 02:08:11.098966457 +0000 @@ -420,16 +420,29 @@ struct mount* vq_vptomp(struct vnode *vp) { - /* XXX: vp->v_pfsmp may point to a freed structure - * we use mountlist_exists() to check if it is valid - * before using it */ - if ((vp->v_pfsmp != NULL) && (mountlist_exists(vp->v_pfsmp))) { - /* This is a PFS, use a copy of the real mp */ - return vp->v_pfsmp; - } else { - /* Not a PFS or a PFS beeing unmounted */ - return vp->v_mount; + struct mount *mp; + + /* + * XXX: vp->v_pfsmp may point to a mount that has already been + * unmounted and freed, or to a *different* mount that reused + * the freed struct mount's address. Take a real hold and + * validate the generation cookie before using it. + * + * Returns a held mount; the caller must mount_drop() it. + */ + if (vp->v_pfsmp != NULL) { + mp = mountlist_hold(vp->v_pfsmp); + if (mp != NULL) { + if (mp->mnt_cookie == vp->v_pfsmp_cookie) + return mp; /* held */ + mount_drop(mp); /* different mount reused the addr */ + } } + /* Not a PFS or the PFS is beeing unmounted */ + mp = vp->v_mount; + mount_hold(mp); + + return mp; } int --- a/sys/kern/vfs_vnops.c 2026-09-01 02:05:26.429046900 +0000 +++ b/sys/kern/vfs_vnops.c 2026-09-01 02:05:37.624905461 +0000 @@ -324,6 +324,7 @@ goto bad; mp = vq_vptomp(vp); VFS_ACCOUNT(mp, vap->va_uid, vap->va_gid, -osize); + mount_drop(mp); } /* --- a/sys/kern/vfs_vopops.c 2026-09-01 02:05:26.429046900 +0000 +++ b/sys/kern/vfs_vopops.c 2026-09-01 02:05:42.544843305 +0000 @@ -456,7 +456,7 @@ int error, do_accounting = 0; struct vattr va; uint64_t size_before=0, size_after=0; - struct mount *mp; + struct mount *mp = NULL; uint64_t offset, delta; ap.a_head.a_desc = &vop_write_desc; @@ -493,9 +493,12 @@ } DO_OPS(ops, error, &ap, vop_write); if ((error == 0) && do_accounting) { + KKASSERT(mp != NULL); VFS_ACCOUNT(mp, va.va_uid, va.va_gid, size_after - size_before); } done: + if (mp) + mount_drop(mp); VFS_MPUNLOCK(); return(error); --- a/sys/kern/vfs_cache.c 2026-09-01 02:07:20.219609296 +0000 +++ b/sys/kern/vfs_cache.c 2026-09-01 02:07:29.823487957 +0000 @@ -1384,8 +1384,10 @@ * implementation */ if (mp) { - if (strncmp(mp->mnt_stat.f_fstypename, "null", 5) == 0) + if (strncmp(mp->mnt_stat.f_fstypename, "null", 5) == 0) { vp->v_pfsmp = mp; + vp->v_pfsmp_cookie = mp->mnt_cookie; + } } } else { /* --- a/sys/sys/mount.h 2026-09-01 02:05:26.433046850 +0000 +++ b/sys/sys/mount.h 2026-09-01 02:07:25.379544103 +0000 @@ -219,6 +219,7 @@ struct vfsconf *mnt_vfc; /* configuration info */ u_int mnt_namecache_gen; /* ++ to clear negative hits */ u_int mnt_pbuf_count; /* pbuf usage limit */ + uint64_t mnt_cookie; /* unique generation cookie */ struct vnode *mnt_syncer; /* syncer vnode */ struct syncer_ctx *mnt_syncer_ctx; /* syncer process context */ struct vnodelst mnt_nvnodelist; /* list of vnodes this mount */ @@ -806,6 +807,7 @@ struct mount *mountlist_boot_getfirst(void); void mountlist_remove(struct mount *mp); int mountlist_exists(struct mount *mp); +struct mount *mountlist_hold(struct mount *mp); int mountlist_scan(int (*callback)(struct mount *, void *), void *, int); struct mount *mount_get_by_nc(struct namecache *ncp); #else /* !_KERNEL */ --- a/sys/sys/vnode.h 2026-09-01 02:07:20.219609296 +0000 +++ b/sys/sys/vnode.h 2026-09-01 02:07:23.955562095 +0000 @@ -191,6 +191,7 @@ } v_pollinfo; struct vmresident *v_resident; /* optional vmresident */ struct mount *v_pfsmp; /* real mp for pfs/nullfs mt */ + uint64_t v_pfsmp_cookie; /* generation of v_pfsmp */ struct timespec v_lastwrite_ts; /* async mmap flush ts */ }; #define v_socket v_un.vu_socket