Illumos 5531 - NULL pointer dereference in dsl_prop_get_ds()
5531 NULL pointer dereference in dsl_prop_get_ds() Author: Justin T. Gibbs <justing@spectralogic.com> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed by: Dan McDonald <danmcd@omniti.com> Reviewed by: George Wilson <george@delphix.com> Reviewed by: Bayard Bell <buffer.g.overflow@gmail.com> Approved by: Robert Mustacchi <rm@joyent.com> References: https://www.illumos.org/issues/5531 https://github.com/illumos/illumos-gate/commit/e57a022 Ported-by: Chris Dunlop <chris@onthe.net.au> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
parent
0c66c32d1d
commit
6ebebaceb1
|
@ -262,12 +262,15 @@ int dbuf_hold_impl(struct dnode *dn, uint8_t level, uint64_t blkid, int create,
|
||||||
void dbuf_prefetch(struct dnode *dn, uint64_t blkid, zio_priority_t prio);
|
void dbuf_prefetch(struct dnode *dn, uint64_t blkid, zio_priority_t prio);
|
||||||
|
|
||||||
void dbuf_add_ref(dmu_buf_impl_t *db, void *tag);
|
void dbuf_add_ref(dmu_buf_impl_t *db, void *tag);
|
||||||
|
boolean_t dbuf_try_add_ref(dmu_buf_t *db, objset_t *os, uint64_t obj,
|
||||||
|
uint64_t blkid, void *tag);
|
||||||
uint64_t dbuf_refcount(dmu_buf_impl_t *db);
|
uint64_t dbuf_refcount(dmu_buf_impl_t *db);
|
||||||
|
|
||||||
void dbuf_rele(dmu_buf_impl_t *db, void *tag);
|
void dbuf_rele(dmu_buf_impl_t *db, void *tag);
|
||||||
void dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag);
|
void dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag);
|
||||||
|
|
||||||
dmu_buf_impl_t *dbuf_find(struct dnode *dn, uint8_t level, uint64_t blkid);
|
dmu_buf_impl_t *dbuf_find(struct objset *os, uint64_t object, uint8_t level,
|
||||||
|
uint64_t blkid);
|
||||||
|
|
||||||
int dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags);
|
int dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags);
|
||||||
void dmu_buf_will_not_fill(dmu_buf_t *db, dmu_tx_t *tx);
|
void dmu_buf_will_not_fill(dmu_buf_t *db, dmu_tx_t *tx);
|
||||||
|
|
|
@ -454,7 +454,23 @@ int dmu_spill_hold_existing(dmu_buf_t *bonus, void *tag, dmu_buf_t **dbp);
|
||||||
*/
|
*/
|
||||||
int dmu_buf_hold(objset_t *os, uint64_t object, uint64_t offset,
|
int dmu_buf_hold(objset_t *os, uint64_t object, uint64_t offset,
|
||||||
void *tag, dmu_buf_t **, int flags);
|
void *tag, dmu_buf_t **, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a reference to a dmu buffer that has already been held via
|
||||||
|
* dmu_buf_hold() in the current context.
|
||||||
|
*/
|
||||||
void dmu_buf_add_ref(dmu_buf_t *db, void* tag);
|
void dmu_buf_add_ref(dmu_buf_t *db, void* tag);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt to add a reference to a dmu buffer that is in an unknown state,
|
||||||
|
* using a pointer that may have been invalidated by eviction processing.
|
||||||
|
* The request will succeed if the passed in dbuf still represents the
|
||||||
|
* same os/object/blkid, is ineligible for eviction, and has at least
|
||||||
|
* one hold by a user other than the syncer.
|
||||||
|
*/
|
||||||
|
boolean_t dmu_buf_try_add_ref(dmu_buf_t *, objset_t *os, uint64_t object,
|
||||||
|
uint64_t blkid, void *tag);
|
||||||
|
|
||||||
void dmu_buf_rele(dmu_buf_t *db, void *tag);
|
void dmu_buf_rele(dmu_buf_t *db, void *tag);
|
||||||
uint64_t dmu_buf_refcount(dmu_buf_t *db);
|
uint64_t dmu_buf_refcount(dmu_buf_t *db);
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,8 @@ dsl_dataset_phys(dsl_dataset_t *ds)
|
||||||
|
|
||||||
int dsl_dataset_hold(struct dsl_pool *dp, const char *name, void *tag,
|
int dsl_dataset_hold(struct dsl_pool *dp, const char *name, void *tag,
|
||||||
dsl_dataset_t **dsp);
|
dsl_dataset_t **dsp);
|
||||||
|
boolean_t dsl_dataset_try_add_ref(struct dsl_pool *dp, dsl_dataset_t *ds,
|
||||||
|
void *tag);
|
||||||
int dsl_dataset_hold_obj(struct dsl_pool *dp, uint64_t dsobj, void *tag,
|
int dsl_dataset_hold_obj(struct dsl_pool *dp, uint64_t dsobj, void *tag,
|
||||||
dsl_dataset_t **);
|
dsl_dataset_t **);
|
||||||
void dsl_dataset_rele(dsl_dataset_t *ds, void *tag);
|
void dsl_dataset_rele(dsl_dataset_t *ds, void *tag);
|
||||||
|
|
|
@ -149,16 +149,13 @@ dbuf_hash(void *os, uint64_t obj, uint8_t lvl, uint64_t blkid)
|
||||||
(dbuf)->db_blkid == (blkid))
|
(dbuf)->db_blkid == (blkid))
|
||||||
|
|
||||||
dmu_buf_impl_t *
|
dmu_buf_impl_t *
|
||||||
dbuf_find(dnode_t *dn, uint8_t level, uint64_t blkid)
|
dbuf_find(objset_t *os, uint64_t obj, uint8_t level, uint64_t blkid)
|
||||||
{
|
{
|
||||||
dbuf_hash_table_t *h = &dbuf_hash_table;
|
dbuf_hash_table_t *h = &dbuf_hash_table;
|
||||||
objset_t *os = dn->dn_objset;
|
|
||||||
uint64_t obj;
|
|
||||||
uint64_t hv;
|
uint64_t hv;
|
||||||
uint64_t idx;
|
uint64_t idx;
|
||||||
dmu_buf_impl_t *db;
|
dmu_buf_impl_t *db;
|
||||||
|
|
||||||
obj = dn->dn_object;
|
|
||||||
hv = DBUF_HASH(os, obj, level, blkid);
|
hv = DBUF_HASH(os, obj, level, blkid);
|
||||||
idx = hv & h->hash_table_mask;
|
idx = hv & h->hash_table_mask;
|
||||||
|
|
||||||
|
@ -177,6 +174,24 @@ dbuf_find(dnode_t *dn, uint8_t level, uint64_t blkid)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dmu_buf_impl_t *
|
||||||
|
dbuf_find_bonus(objset_t *os, uint64_t object)
|
||||||
|
{
|
||||||
|
dnode_t *dn;
|
||||||
|
dmu_buf_impl_t *db = NULL;
|
||||||
|
|
||||||
|
if (dnode_hold(os, object, FTAG, &dn) == 0) {
|
||||||
|
rw_enter(&dn->dn_struct_rwlock, RW_READER);
|
||||||
|
if (dn->dn_bonus != NULL) {
|
||||||
|
db = dn->dn_bonus;
|
||||||
|
mutex_enter(&db->db_mtx);
|
||||||
|
}
|
||||||
|
rw_exit(&dn->dn_struct_rwlock);
|
||||||
|
dnode_rele(dn, FTAG);
|
||||||
|
}
|
||||||
|
return (db);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert an entry into the hash table. If there is already an element
|
* Insert an entry into the hash table. If there is already an element
|
||||||
* equal to elem in the hash table, then the already existing element
|
* equal to elem in the hash table, then the already existing element
|
||||||
|
@ -2000,7 +2015,7 @@ dbuf_prefetch(dnode_t *dn, uint64_t blkid, zio_priority_t prio)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* dbuf_find() returns with db_mtx held */
|
/* dbuf_find() returns with db_mtx held */
|
||||||
if ((db = dbuf_find(dn, 0, blkid))) {
|
if ((db = dbuf_find(dn->dn_objset, dn->dn_object, 0, blkid))) {
|
||||||
/*
|
/*
|
||||||
* This dbuf is already in the cache. We assume that
|
* This dbuf is already in the cache. We assume that
|
||||||
* it is already CACHED, or else about to be either
|
* it is already CACHED, or else about to be either
|
||||||
|
@ -2048,7 +2063,8 @@ __dbuf_hold_impl(struct dbuf_hold_impl_data *dh)
|
||||||
*(dh->dh_dbp) = NULL;
|
*(dh->dh_dbp) = NULL;
|
||||||
top:
|
top:
|
||||||
/* dbuf_find() returns with db_mtx held */
|
/* dbuf_find() returns with db_mtx held */
|
||||||
dh->dh_db = dbuf_find(dh->dh_dn, dh->dh_level, dh->dh_blkid);
|
dh->dh_db = dbuf_find(dh->dh_dn->dn_objset, dh->dh_dn->dn_object,
|
||||||
|
dh->dh_level, dh->dh_blkid);
|
||||||
|
|
||||||
if (dh->dh_db == NULL) {
|
if (dh->dh_db == NULL) {
|
||||||
dh->dh_bp = NULL;
|
dh->dh_bp = NULL;
|
||||||
|
@ -2228,6 +2244,30 @@ dbuf_add_ref(dmu_buf_impl_t *db, void *tag)
|
||||||
VERIFY(refcount_add(&db->db_holds, tag) > 1);
|
VERIFY(refcount_add(&db->db_holds, tag) > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma weak dmu_buf_try_add_ref = dbuf_try_add_ref
|
||||||
|
boolean_t
|
||||||
|
dbuf_try_add_ref(dmu_buf_t *db_fake, objset_t *os, uint64_t obj, uint64_t blkid,
|
||||||
|
void *tag)
|
||||||
|
{
|
||||||
|
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
||||||
|
dmu_buf_impl_t *found_db;
|
||||||
|
boolean_t result = B_FALSE;
|
||||||
|
|
||||||
|
if (db->db_blkid == DMU_BONUS_BLKID)
|
||||||
|
found_db = dbuf_find_bonus(os, obj);
|
||||||
|
else
|
||||||
|
found_db = dbuf_find(os, obj, 0, blkid);
|
||||||
|
|
||||||
|
if (found_db != NULL) {
|
||||||
|
if (db == found_db && dbuf_refcount(db) > db->db_dirtycnt) {
|
||||||
|
(void) refcount_add(&db->db_holds, tag);
|
||||||
|
result = B_TRUE;
|
||||||
|
}
|
||||||
|
mutex_exit(&db->db_mtx);
|
||||||
|
}
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you call dbuf_rele() you had better not be referencing the dnode handle
|
* If you call dbuf_rele() you had better not be referencing the dnode handle
|
||||||
* unless you have some other direct or indirect hold on the dnode. (An indirect
|
* unless you have some other direct or indirect hold on the dnode. (An indirect
|
||||||
|
|
|
@ -77,7 +77,8 @@ dnode_increase_indirection(dnode_t *dn, dmu_tx_t *tx)
|
||||||
|
|
||||||
/* set dbuf's parent pointers to new indirect buf */
|
/* set dbuf's parent pointers to new indirect buf */
|
||||||
for (i = 0; i < nblkptr; i++) {
|
for (i = 0; i < nblkptr; i++) {
|
||||||
dmu_buf_impl_t *child = dbuf_find(dn, old_toplvl, i);
|
dmu_buf_impl_t *child =
|
||||||
|
dbuf_find(dn->dn_objset, dn->dn_object, old_toplvl, i);
|
||||||
|
|
||||||
if (child == NULL)
|
if (child == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -351,6 +351,13 @@ dsl_dataset_snap_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx,
|
||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean_t
|
||||||
|
dsl_dataset_try_add_ref(dsl_pool_t *dp, dsl_dataset_t *ds, void *tag)
|
||||||
|
{
|
||||||
|
return (dmu_buf_try_add_ref(ds->ds_dbuf, dp->dp_meta_objset,
|
||||||
|
ds->ds_object, DMU_BONUS_BLKID, tag));
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||||
dsl_dataset_t **dsp)
|
dsl_dataset_t **dsp)
|
||||||
|
|
|
@ -441,9 +441,31 @@ dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
|
||||||
cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback entries do not have holds on their datasets
|
||||||
|
* so that datasets with registered callbacks are still
|
||||||
|
* eligible for eviction. Unlike operations on callbacks
|
||||||
|
* for a single dataset, we are performing a recursive
|
||||||
|
* descent of related datasets and the calling context
|
||||||
|
* for this iteration only has a dataset hold on the root.
|
||||||
|
* Without a hold, the callback's pointer to the dataset
|
||||||
|
* could be invalidated by eviction at any time.
|
||||||
|
*
|
||||||
|
* Use dsl_dataset_try_add_ref() to verify that the
|
||||||
|
* dataset has not begun eviction processing and to
|
||||||
|
* prevent eviction from occurring for the duration
|
||||||
|
* of the callback. If the hold attempt fails, this
|
||||||
|
* object is already being evicted and the callback can
|
||||||
|
* be safely ignored.
|
||||||
|
*/
|
||||||
|
if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_propname,
|
if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_propname,
|
||||||
sizeof (value), 1, &value, NULL) == 0)
|
sizeof (value), 1, &value, NULL) == 0)
|
||||||
cbr->cbr_func(cbr->cbr_arg, value);
|
cbr->cbr_func(cbr->cbr_arg, value);
|
||||||
|
|
||||||
|
dsl_dataset_rele(cbr->cbr_ds, FTAG);
|
||||||
}
|
}
|
||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
|
|
||||||
|
@ -496,19 +518,28 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
|
||||||
mutex_enter(&dd->dd_lock);
|
mutex_enter(&dd->dd_lock);
|
||||||
for (cbr = list_head(&dd->dd_prop_cbs); cbr;
|
for (cbr = list_head(&dd->dd_prop_cbs); cbr;
|
||||||
cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
cbr = list_next(&dd->dd_prop_cbs, cbr)) {
|
||||||
uint64_t propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj;
|
uint64_t propobj;
|
||||||
|
|
||||||
if (strcmp(cbr->cbr_propname, propname) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the property is set on this ds, then it is not
|
* cbr->cbf_ds may be invalidated due to eviction,
|
||||||
* inherited here; don't call the callback.
|
* requiring the use of dsl_dataset_try_add_ref().
|
||||||
|
* See comment block in dsl_prop_notify_all_cb()
|
||||||
|
* for details.
|
||||||
*/
|
*/
|
||||||
if (propobj && 0 == zap_contains(mos, propobj, propname))
|
if (strcmp(cbr->cbr_propname, propname) != 0 ||
|
||||||
|
!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cbr->cbr_func(cbr->cbr_arg, value);
|
propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the property is not set on this ds, then it is
|
||||||
|
* inherited here; call the callback.
|
||||||
|
*/
|
||||||
|
if (propobj == 0 || zap_contains(mos, propobj, propname) != 0)
|
||||||
|
cbr->cbr_func(cbr->cbr_arg, value);
|
||||||
|
|
||||||
|
dsl_dataset_rele(cbr->cbr_ds, FTAG);
|
||||||
}
|
}
|
||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue