From f848a9dc7f0856da0a822d3bf19145ef321c8f3e Mon Sep 17 00:00:00 2001 From: Axel Gembe Date: Fri, 13 Oct 2023 18:20:08 +0700 Subject: [PATCH] Fix use-after-free after unmount During unmount `zfsvfs` is destroyed and the pointer is zeroed in the VCB. There is however still a copy of the pointer in the DCB. Windows can still call into `zfs_AcquireForLazyWrite` through `CcMgr` after unmount and this would use the already freed `zfsvfs` pointer. To fix this we set the pointer to zero in the DCB and add a zero check in `zfs_AcquireForLazyWrite`. Signed-off-by: Axel Gembe --- module/os/windows/zfs/zfs_vfsops.c | 10 ++++++++++ module/os/windows/zfs/zfs_vnops_windows.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/module/os/windows/zfs/zfs_vfsops.c b/module/os/windows/zfs/zfs_vfsops.c index 613cf3a3c2f1..d1755be61170 100644 --- a/module/os/windows/zfs/zfs_vfsops.c +++ b/module/os/windows/zfs/zfs_vfsops.c @@ -1565,6 +1565,7 @@ zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting) int zfs_vfs_unmount(struct mount *mp, int mntflags, vfs_context_t context) { + mount_t *mp_dcb = NULL; zfsvfs_t *zfsvfs = vfs_fsprivate(mp); objset_t *os; char osname[MAXNAMELEN]; @@ -1670,6 +1671,15 @@ zfs_vfs_unmount(struct mount *mp, int mntflags, vfs_context_t context) dmu_objset_disown(os, B_TRUE, zfsvfs); } + mp_dcb = mp->parent_device; + if (likely(mp_dcb != NULL)) { + if (mp_dcb->type == MOUNT_TYPE_DCB) { + // dcb also has a reference to zfsvfs, we need to remove that + // before it is freed. + vfs_setfsprivate(mp_dcb, NULL); + } + } + zfs_freevfs(zfsvfs->z_vfs); return (0); diff --git a/module/os/windows/zfs/zfs_vnops_windows.c b/module/os/windows/zfs/zfs_vnops_windows.c index 349dfa1a844c..07b1546f38a9 100644 --- a/module/os/windows/zfs/zfs_vnops_windows.c +++ b/module/os/windows/zfs/zfs_vnops_windows.c @@ -132,6 +132,11 @@ zfs_AcquireForLazyWrite(void *Context, BOOLEAN Wait) struct vnode *vp = fo->FsContext; dprintf("%s:fo %p\n", __func__, fo); + if (unlikely(zfsvfs == NULL)) { + dprintf("%s: fo %p already freed zfsvfs\n", __func__, fo); + return (FALSE); + } + /* Confirm we are mounted, and stop unmounting */ if (vfs_busy(zfsvfs->z_vfs, 0) != 0) return (FALSE);