diff options
author | Gunnar Beutner <[email protected]> | 2011-04-14 22:07:24 +0200 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2011-04-21 13:48:01 -0700 |
commit | 36df284366caa77cb40083d2e6bcce02274e2f05 (patch) | |
tree | defd65b3ff2f00226f8fb0d80e6fcc438af144b5 /module | |
parent | d247f2a3cc24e8127ec1c5055bf0cd6c39c09add (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
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/zfs_znode.c | 24 |
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. * |