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 <behlendorf1@llnl.gov>
Closes #180
This commit is contained in:
Gunnar Beutner 2011-04-14 22:07:24 +02:00 committed by Brian Behlendorf
parent d247f2a3cc
commit 36df284366

View File

@ -811,14 +811,19 @@ zfs_zget(zfs_sb_t *zsb, uint64_t obj_num, znode_t **zpp)
znode_t *zp; znode_t *zp;
int err; int err;
sa_handle_t *hdl; sa_handle_t *hdl;
struct inode *ip;
*zpp = NULL; *zpp = NULL;
again:
ip = ilookup(zsb->z_sb, obj_num);
ZFS_OBJ_HOLD_ENTER(zsb, obj_num); ZFS_OBJ_HOLD_ENTER(zsb, obj_num);
err = sa_buf_hold(zsb->z_os, obj_num, NULL, &db); err = sa_buf_hold(zsb->z_os, obj_num, NULL, &db);
if (err) { if (err) {
ZFS_OBJ_HOLD_EXIT(zsb, obj_num); ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
iput(ip);
return (err); 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)))) { doi.doi_bonus_size < sizeof (znode_phys_t)))) {
sa_buf_rele(db, NULL); sa_buf_rele(db, NULL);
ZFS_OBJ_HOLD_EXIT(zsb, obj_num); ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
iput(ip);
return (EINVAL); return (EINVAL);
} }
hdl = dmu_buf_get_user(db); hdl = dmu_buf_get_user(db);
if (hdl != NULL) { 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 * 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); sa_buf_rele(db, NULL);
mutex_exit(&zp->z_lock); mutex_exit(&zp->z_lock);
ZFS_OBJ_HOLD_EXIT(zsb, obj_num); ZFS_OBJ_HOLD_EXIT(zsb, obj_num);
iput(ip);
return (err); return (err);
} }
ASSERT3P(ip, ==, NULL);
/* /*
* Not found create new znode/vnode but only if file exists. * Not found create new znode/vnode but only if file exists.
* *