From 32c8c946ea3228de86946fdd637c85eeeff1726a Mon Sep 17 00:00:00 2001 From: Chunwei Chen Date: Fri, 25 Mar 2016 15:21:56 -0400 Subject: [PATCH] OpenZFS 6842 - Fix empty xattr dir causing lockup Reviewed by: Brian Behlendorf Reviewed by: Dan McDonald Reviewed by: Matthew Ahrens Approved by: Robert Mustacchi Ported-by: Denys Rtveliashvili Signed-off-by: Brian Behlendorf An initial version of this patch was applied in commit 29572cc and subsequently refined upstream. Since the implementations do not conflict with each other both are left applied for now. OpenZFS-issue: https://www.illumos.org/issues/6842 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/02525cd Closes #4615 --- module/zfs/zap.c | 8 +++++++- module/zfs/zap_micro.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/module/zfs/zap.c b/module/zfs/zap.c index 9d06cce74..454b4be62 100644 --- a/module/zfs/zap.c +++ b/module/zfs/zap.c @@ -585,7 +585,13 @@ zap_deref_leaf(zap_t *zap, uint64_t h, dmu_tx_t *tx, krw_t lt, zap_leaf_t **lp) ASSERT(zap->zap_dbuf == NULL || zap_f_phys(zap) == zap->zap_dbuf->db_data); - ASSERT3U(zap_f_phys(zap)->zap_magic, ==, ZAP_MAGIC); + + /* Reality check for corrupt zap objects (leaf or header). */ + if ((zap_f_phys(zap)->zap_block_type != ZBT_LEAF && + zap_f_phys(zap)->zap_block_type != ZBT_HEADER) || + zap_f_phys(zap)->zap_magic != ZAP_MAGIC) { + return (SET_ERROR(EIO)); + } idx = ZAP_HASH_IDX(h, zap_f_phys(zap)->zap_ptrtbl.zt_shift); err = zap_idx_to_blk(zap, idx, &blk); if (err != 0) diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c index 85b465b05..3faf27ce3 100644 --- a/module/zfs/zap_micro.c +++ b/module/zfs/zap_micro.c @@ -366,6 +366,9 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) zap_t *winner; zap_t *zap; int i; + uint64_t *zap_hdr = (uint64_t *)db->db_data; + uint64_t zap_block_type = zap_hdr[0]; + uint64_t zap_magic = zap_hdr[1]; ASSERT3U(MZAP_ENT_LEN, ==, sizeof (mzap_ent_phys_t)); @@ -376,9 +379,13 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) zap->zap_object = obj; zap->zap_dbuf = db; - if (*(uint64_t *)db->db_data != ZBT_MICRO) { + if (zap_block_type != ZBT_MICRO) { mutex_init(&zap->zap_f.zap_num_entries_mtx, 0, 0, 0); zap->zap_f.zap_block_shift = highbit64(db->db_size) - 1; + if (zap_block_type != ZBT_HEADER || zap_magic != ZAP_MAGIC) { + winner = NULL; /* No actual winner here... */ + goto handle_winner; + } } else { zap->zap_ismicro = TRUE; } @@ -391,14 +398,8 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) dmu_buf_init_user(&zap->zap_dbu, zap_evict, &zap->zap_dbuf); winner = dmu_buf_set_user(db, &zap->zap_dbu); - if (winner != NULL) { - rw_exit(&zap->zap_rwlock); - rw_destroy(&zap->zap_rwlock); - if (!zap->zap_ismicro) - mutex_destroy(&zap->zap_f.zap_num_entries_mtx); - kmem_free(zap, sizeof (zap_t)); - return (winner); - } + if (winner != NULL) + goto handle_winner; if (zap->zap_ismicro) { zap->zap_salt = zap_m_phys(zap)->mz_salt; @@ -445,6 +446,14 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) } rw_exit(&zap->zap_rwlock); return (zap); + +handle_winner: + rw_exit(&zap->zap_rwlock); + rw_destroy(&zap->zap_rwlock); + if (!zap->zap_ismicro) + mutex_destroy(&zap->zap_f.zap_num_entries_mtx); + kmem_free(zap, sizeof (zap_t)); + return (winner); } int @@ -468,8 +477,17 @@ zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx, return (SET_ERROR(EINVAL)); zap = dmu_buf_get_user(db); - if (zap == NULL) + if (zap == NULL) { zap = mzap_open(os, obj, db); + if (zap == NULL) { + /* + * mzap_open() didn't like what it saw on-disk. + * Check for corruption! + */ + dmu_buf_rele(db, NULL); + return (SET_ERROR(EIO)); + } + } /* * We're checking zap_ismicro without the lock held, in order to