summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Beutner <[email protected]>2011-04-14 22:07:24 +0200
committerBrian Behlendorf <[email protected]>2011-04-21 13:48:01 -0700
commit36df284366caa77cb40083d2e6bcce02274e2f05 (patch)
treedefd65b3ff2f00226f8fb0d80e6fcc438af144b5
parentd247f2a3cc24e8127ec1c5055bf0cd6c39c09add (diff)
Fixed a use-after-free bug in zfs_zget().
Fixed a bug where zfs_zget could access a stale znode pointer when the inode had already been removed from the inode cache via iput -> iput_final -> ... -> zfs_zinactive but the corresponding SA handle was still alive. Signed-off-by: Brian Behlendorf <[email protected]> Closes #180
-rw-r--r--module/zfs/zfs_znode.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c
index f9ea2a0eb..edfb86009 100644
--- a/module/zfs/zfs_znode.c
+++ b/module/zfs/zfs_znode.c
@@ -811,14 +811,19 @@ zfs_zget(zfs_sb_t *zsb, uint64_t obj_num, znode_t **zpp)
znode_t *zp;
int err;
sa_handle_t *hdl;
+ struct inode *ip;
*zpp = NULL;
+again:
+ ip = ilookup(zsb->z_sb, obj_num);
+
ZFS_OBJ_HOLD_ENTER(zsb, obj_num);
err = sa_buf_hold(zsb->z_os, obj_num, NULL, &db);
if (err) {
ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
+ iput(ip);
return (err);
}
@@ -829,13 +834,27 @@ zfs_zget(zfs_sb_t *zsb, uint64_t obj_num, znode_t **zpp)
doi.doi_bonus_size < sizeof (znode_phys_t)))) {
sa_buf_rele(db, NULL);
ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
+ iput(ip);
return (EINVAL);
}
hdl = dmu_buf_get_user(db);
if (hdl != NULL) {
- zp = sa_get_userdata(hdl);
+ if (ip == NULL) {
+ /*
+ * ilookup returned NULL, which means
+ * the znode is dying - but the SA handle isn't
+ * quite dead yet, we need to drop any locks
+ * we're holding, re-schedule the task and try again.
+ */
+ sa_buf_rele(db, NULL);
+ ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
+
+ schedule();
+ goto again;
+ }
+ zp = sa_get_userdata(hdl);
/*
* Since "SA" does immediate eviction we
@@ -857,9 +876,12 @@ zfs_zget(zfs_sb_t *zsb, uint64_t obj_num, znode_t **zpp)
sa_buf_rele(db, NULL);
mutex_exit(&zp->z_lock);
ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
+ iput(ip);
return (err);
}
+ ASSERT3P(ip, ==, NULL);
+
/*
* Not found create new znode/vnode but only if file exists.
*