diff options
author | Chunwei Chen <[email protected]> | 2023-01-19 16:59:05 -0800 |
---|---|---|
committer | GitHub <[email protected]> | 2023-01-19 16:59:05 -0800 |
commit | c6dab6dd39214d587c7013e8c6dfeb085e3eb41c (patch) | |
tree | c0ec00fc4945800b7b8103d2abb3a42a26e9763e /module/os/linux/zfs/zfs_dir.c | |
parent | a379083d9f2bb9dd80f4636e593bcb2c1d94d11b (diff) |
Fix unprotected zfs_znode_dmu_fini
In original code, zfs_znode_dmu_fini is called in zfs_rmnode without
zfs_znode_hold_enter. It seems to assume it's ok to do so when the znode
is unlinked. However this assumption is not correct, as zfs_zget can be
called by NFS through zpl_fh_to_dentry as pointed out by Christian in
https://github.com/openzfs/zfs/pull/12767, which could result in a
use-after-free bug.
Reviewed-by: Brian Behlendorf <[email protected]>
Co-authored-by: Ryan Moeller <[email protected]>
Signed-off-by: Chunwei Chen <[email protected]>
Signed-off-by: Ryan Moeller <[email protected]>
Closes #12767
Closes #14364
Diffstat (limited to 'module/os/linux/zfs/zfs_dir.c')
-rw-r--r-- | module/os/linux/zfs/zfs_dir.c | 9 |
1 files changed, 8 insertions, 1 deletions
diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c index 09eac37f9..1fec4ea09 100644 --- a/module/os/linux/zfs/zfs_dir.c +++ b/module/os/linux/zfs/zfs_dir.c @@ -649,6 +649,8 @@ zfs_rmnode(znode_t *zp) objset_t *os = zfsvfs->z_os; znode_t *xzp = NULL; dmu_tx_t *tx; + znode_hold_t *zh; + uint64_t z_id = zp->z_id; uint64_t acl_obj; uint64_t xattr_obj; uint64_t links; @@ -666,8 +668,9 @@ zfs_rmnode(znode_t *zp) * Not enough space to delete some xattrs. * Leave it in the unlinked set. */ + zh = zfs_znode_hold_enter(zfsvfs, z_id); zfs_znode_dmu_fini(zp); - + zfs_znode_hold_exit(zfsvfs, zh); return; } } @@ -686,7 +689,9 @@ zfs_rmnode(znode_t *zp) * Not enough space or we were interrupted by unmount. * Leave the file in the unlinked set. */ + zh = zfs_znode_hold_enter(zfsvfs, z_id); zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); return; } } @@ -726,7 +731,9 @@ zfs_rmnode(znode_t *zp) * which point we'll call zfs_unlinked_drain() to process it). */ dmu_tx_abort(tx); + zh = zfs_znode_hold_enter(zfsvfs, z_id); zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); goto out; } |