summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Gorrod <alexander.gorrod@mongodb.com>2015-11-20 14:42:34 +1100
committerAlex Gorrod <alexg@wiredtiger.com>2015-12-02 05:40:57 +0000
commit1da2d3a517392d71e13b7ecdeaa97b01a2627d2d (patch)
tree5ddb27802573ab2f0a212eae8775412d3b439062
parent42282959f7d9928552456cf41186f115d412c7ca (diff)
downloadmongo-1da2d3a517392d71e13b7ecdeaa97b01a2627d2d.tar.gz
SERVER-21568 Fix a use after-free
(cherry picked from commit 2d01a566) Conflicts: src/btree/bt_split.c
-rw-r--r--src/btree/bt_delete.c12
-rw-r--r--src/btree/bt_split.c50
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