diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 0000000..1111111 100644 @@ -1242,6 +1242,15 @@ if (ncp == mp->mnt_ncmountpt.ncp) break; } + /* + * Match the hold contract of vfs_getvfs() and mountlist_scan(): + * the returned mp (if any) is held and the caller must drop it via + * mount_drop(). Without this, the caller (cache_fullpath) would + * dereference mp->mnt_ncmounton after we release mountlist_token, + * racing dounmount()->mount_drop()->kfree() and causing a UAF. + */ + if (mp) + mount_hold(mp); lwkt_reltoken(&mountlist_token); return (mp); diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 0000000..1111111 100644 @@ -5202,6 +5202,7 @@ mp = nch.mount; while (ncp && (ncp != fd_nrdir.ncp || mp != fd_nrdir.mount)) { + struct mount *held_mp = NULL; new_mp = NULL; /* @@ -5211,13 +5212,24 @@ * ncp. */ if (guess && (ncp->nc_flag & NCF_ISMOUNTPT)) { - new_mp = mount_get_by_nc(ncp); + /* + * Post-fix in vfs_mount.c: mount_get_by_nc() now + * returns mp held; we must mount_drop() it once we + * are done dereferencing new_mp->mnt_ncmounton. + */ + held_mp = mount_get_by_nc(ncp); + new_mp = held_mp; } /* * While traversing upwards if we encounter the root * of the current mount we have to skip to the mount point. */ if (ncp == mp->mnt_ncmountpt.ncp) { + /* Override: drop the held mp from mount_get_by_nc, if any. */ + if (held_mp) { + mount_drop(held_mp); + held_mp = NULL; + } new_mp = mp; } if (new_mp) { @@ -5227,6 +5239,8 @@ if (ncp) _cache_hold(ncp); mp = nch.mount; + if (held_mp) + mount_drop(held_mp); continue; }