diff options
author | Alex Gorrod <alexander.gorrod@mongodb.com> | 2015-11-20 14:42:34 +1100 |
---|---|---|
committer | Alex Gorrod <alexg@wiredtiger.com> | 2015-12-02 05:40:57 +0000 |
commit | 1da2d3a517392d71e13b7ecdeaa97b01a2627d2d (patch) | |
tree | 5ddb27802573ab2f0a212eae8775412d3b439062 /src | |
parent | 42282959f7d9928552456cf41186f115d412c7ca (diff) | |
download | mongo-1da2d3a517392d71e13b7ecdeaa97b01a2627d2d.tar.gz |
SERVER-21568 Fix a use after-free
(cherry picked from commit 2d01a566)
Conflicts:
src/btree/bt_split.c
Diffstat (limited to 'src')
-rw-r--r-- | src/btree/bt_delete.c | 12 | ||||
-rw-r--r-- | src/btree/bt_split.c | 50 |
2 files changed, 41 insertions, 21 deletions
diff --git a/src/btree/bt_delete.c b/src/btree/bt_delete.c index 7313e31267f..e19085830bd 100644 --- a/src/btree/bt_delete.c +++ b/src/btree/bt_delete.c @@ -250,6 +250,18 @@ __wt_delete_page_skip(WT_SESSION_IMPL *session, WT_REF *ref, bool visible_all) __wt_txn_visible_all(session, ref->page_del->txnid) : __wt_txn_visible(session, ref->page_del->txnid)); + /* + * The page_del structure can be freed as soon as the delete is stable: + * it is only read when the ref state is WT_REF_DELETED. It is worth + * checking every time we come through because once this is freed, we + * no longer need synchronization to check the ref. + */ + if (skip && ref->page_del != NULL && (visible_all || + __wt_txn_visible_all(session, ref->page_del->txnid))) { + __wt_free(session, ref->page_del->update_list); + __wt_free(session, ref->page_del); + } + WT_PUBLISH(ref->state, WT_REF_DELETED); return (skip); } diff --git a/src/btree/bt_split.c b/src/btree/bt_split.c index 26fed53708c..5fc2c4cd89f 100644 --- a/src/btree/bt_split.c +++ b/src/btree/bt_split.c @@ -1030,6 +1030,16 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, #endif /* + * Page-delete information is only read when the WT_REF state is + * WT_REF_DELETED. The page-delete memory wasn't added to the + * parent's footprint, ignore it here. + */ + if (ref->page_del != NULL) { + __wt_free(session, ref->page_del->update_list); + __wt_free(session, ref->page_del); + } + + /* * Reset the page's original WT_REF field to split. Threads cursoring * through the tree were blocked because that WT_REF state was set to * locked. This update changes the locked state to split, unblocking @@ -1090,19 +1100,14 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, session, split_gen, 0, ikey, size)); parent_decr += size; } - /* - * The page_del structure can be freed immediately: it - * is only read when the ref state is WT_REF_DELETED. - * The size of the structure wasn't added to the parent, - * don't decrement. - */ - if (next_ref->page_del != NULL) { - __wt_free(session, - next_ref->page_del->update_list); - __wt_free(session, next_ref->page_del); - } } + /* + * If this page was fast-truncated, any attached structure + * should have been freed before now. + */ + WT_ASSERT(session, next_ref->page_del == NULL); + WT_TRET(__split_safe_free( session, split_gen, 0, next_ref, sizeof(WT_REF))); parent_decr += sizeof(WT_REF); @@ -1214,21 +1219,24 @@ __split_insert(WT_SESSION_IMPL *session, WT_REF *ref) * The first page in the split is the current page, but we still have * to create a replacement WT_REF, the original WT_REF will be set to * split status and eventually freed. - */ - WT_ERR(__wt_calloc_one(session, &split_ref[0])); - child = split_ref[0]; - *child = *ref; - - /* + * * The new WT_REF is not quite identical: we have to instantiate a key, * and the new reference is visible to readers once the split completes. * - * The key-instantiation code checks for races, clear the key fields so - * we don't trigger them. + * The key-instantiation code checks for races, leave the key fields + * zeroed we don't trigger them. + * + * Don't copy any deleted page state: we may be splitting a page that + * was instantiated after a truncate and that history should not be + * carried onto these new child pages. */ - child->key.recno = 0; - child->key.ikey = NULL; + WT_ERR(__wt_calloc_one(session, &split_ref[0])); + child = split_ref[0]; + child->page = ref->page; + child->home = ref->home; + child->pindex_hint = ref->pindex_hint; child->state = WT_REF_MEM; + child->addr = ref->addr; /* * Copy the first key from the original page into first ref in the new |