diff --git a/changelogs/unreleased/gh-10146-mvcc-ddl-crashes-and-isolation-violation.md b/changelogs/unreleased/gh-10146-mvcc-ddl-crashes-and-isolation-violation.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc0aa0b2520d8616df5aa7e64b21bedebe252d9f
--- /dev/null
+++ b/changelogs/unreleased/gh-10146-mvcc-ddl-crashes-and-isolation-violation.md
@@ -0,0 +1,4 @@
+## bugfix/memtx
+
+* Fixed several bugs when DDL with MVCC enabled could lead to a crash
+  or violate isolation of other transactions (gh-10146).
diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 97b0ee2088ca822fe2fe170be8322019db28241d..8c29c9432c049458afbebbf646221a84d64dd515 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -1202,53 +1202,6 @@ memtx_tx_story_link_top(struct memtx_story *new_top,
 	rlist_splice(&new_link->read_gaps, &old_link->read_gaps);
 }
 
-static void
-memtx_tx_story_unlink_top_on_space_delete(struct memtx_story *story,
-					  uint32_t idx)
-{
-	struct memtx_story_link *link = &story->link[idx];
-	struct memtx_story *old_story = link->older_story;
-	if (old_story != NULL)
-		memtx_tx_story_unlink(story, old_story, idx);
-}
-
-/**
- * Unlink a @a story from history chain in @a index in both directions.
- * Handles case when the story is not on top of the history: simply remove from
- * list.
- */
-static void
-memtx_tx_story_unlink_both_common(struct memtx_story *story,
-				  uint32_t idx)
-{
-	assert(idx < story->index_count);
-	struct memtx_story_link *link = &story->link[idx];
-	struct memtx_story *newer_story = link->newer_story;
-	struct memtx_story *older_story = link->older_story;
-	memtx_tx_story_unlink(newer_story, story, idx);
-	memtx_tx_story_unlink(story, older_story, idx);
-	memtx_tx_story_link(newer_story, older_story, idx);
-}
-
-/**
- * Unlink a @a story from history chain in @a index in both directions.
- * If the story was in the top of history chain - unlink from top. Otherwise,
- * see description of `memtx_tx_story_unlink_both_common`.
- * Since the space is being deleted, we need to simply unlink the story.
- */
-static void
-memtx_tx_story_unlink_both_on_space_delete(struct memtx_story *story,
-					   uint32_t idx)
-{
-	assert(idx < story->index_count);
-	struct memtx_story_link *link = &story->link[idx];
-	if (link->newer_story == NULL) {
-		memtx_tx_story_unlink_top_on_space_delete(story, idx);
-	} else {
-		memtx_tx_story_unlink_both_common(story, idx);
-	}
-}
-
 /**
  * Change the order of stories in history chain.
  */
@@ -2076,28 +2029,6 @@ memtx_tx_history_add_stmt(struct txn_stmt *stmt, struct tuple *old_tuple,
 							result);
 }
 
-/*
- * Relink those who delete this story and make them delete older story.
- */
-static void
-memtx_tx_history_remove_story_del_stmts(struct memtx_story *story)
-{
-	struct memtx_story *old_story = story->link[0].older_story;
-	while (story->del_stmt) {
-		struct txn_stmt *del_stmt = story->del_stmt;
-
-		/* Unlink from old list in any case. */
-		story->del_stmt = del_stmt->next_in_del_list;
-		del_stmt->next_in_del_list = NULL;
-		del_stmt->del_story = NULL;
-
-		/* Link to old story's list. */
-		if (old_story != NULL)
-			memtx_tx_story_link_deleted_by(old_story,
-						       del_stmt);
-	}
-}
-
 /*
  * Abort with conflict all transactions that have read @a story.
  */
@@ -2332,44 +2263,6 @@ memtx_tx_history_rollback_stmt(struct txn_stmt *stmt)
 	assert(stmt->add_story == NULL && stmt->del_story == NULL);
 }
 
-/*
- * Completely remove statement that adds a story.
- */
-static void
-memtx_tx_history_remove_added_story(struct txn_stmt *stmt)
-{
-	assert(stmt->add_story != NULL);
-	assert(stmt->add_story->tuple == stmt->rollback_info.new_tuple);
-	struct memtx_story *story = stmt->add_story;
-	memtx_tx_history_remove_story_del_stmts(story);
-	for (uint32_t i = 0; i < story->index_count; i++)
-		memtx_tx_story_unlink_both_on_space_delete(story, i);
-	memtx_tx_story_unlink_added_by(story, stmt);
-}
-
-/*
- * Completely remove statement that deletes a story.
- */
-static void
-memtx_tx_history_remove_deleted_story(struct txn_stmt *stmt)
-{
-	memtx_tx_story_unlink_deleted_by(stmt->del_story, stmt);
-}
-
-/*
- * Completely (as opposed to rollback) remove statement from history.
- */
-static void
-memtx_tx_history_remove_stmt(struct txn_stmt *stmt)
-{
-	if (stmt->add_story != NULL)
-		memtx_tx_history_remove_added_story(stmt);
-	if (stmt->del_story != NULL)
-		memtx_tx_history_remove_deleted_story(stmt);
-	if (stmt->txn->status == TXN_ABORTED)
-		stmt->engine_savepoint = NULL;
-}
-
 /**
  * Abort or send to read view readers of @a story, except the transaction
  * @a writer that is actually deletes the story.
@@ -2944,9 +2837,10 @@ memtx_tx_invalidate_space(struct space *space, struct txn *active_txn)
 					  struct memtx_story,
 					  in_space_stories);
 		if (story->add_stmt != NULL)
-			memtx_tx_history_remove_stmt(story->add_stmt);
+			memtx_tx_story_unlink_added_by(story, story->add_stmt);
 		while (story->del_stmt != NULL)
-			memtx_tx_history_remove_stmt(story->del_stmt);
+			memtx_tx_story_unlink_deleted_by(story,
+							 story->del_stmt);
 		memtx_tx_story_full_unlink_on_space_delete(story);
 		for (uint32_t i = 0; i < story->index_count; i++) {
 			struct rlist *read_gaps = &story->link[i].read_gaps;
@@ -2972,6 +2866,23 @@ memtx_tx_invalidate_space(struct space *space, struct txn *active_txn)
 		}
 		memtx_tx_story_delete(story);
 	}
+
+	/*
+	 * Phase three: remove savepoints from all affected statements so that
+	 * they won't be rolled back because we already did it. Moreover, they
+	 * could access the old space that is going to be deleted leading to
+	 * use-after-free.
+	 */
+	struct txn *txn;
+	rlist_foreach_entry(txn, &txm.all_txs, in_all_txs) {
+		if (txn->status != TXN_ABORTED || txn->psn != 0)
+			continue;
+		struct txn_stmt *stmt;
+		stailq_foreach_entry(stmt, &txn->stmts, next) {
+			if (stmt->space == space)
+				stmt->engine_savepoint = NULL;
+		}
+	}
 }
 
 /**
diff --git a/test/box-luatest/mvcc_ddl_test.lua b/test/box-luatest/mvcc_ddl_test.lua
index 0279172c55172e170abbc70c839d4f7fbacd956a..7f0381adfa5af6fa391ee738abdb424dca0ff8da 100644
--- a/test/box-luatest/mvcc_ddl_test.lua
+++ b/test/box-luatest/mvcc_ddl_test.lua
@@ -472,3 +472,25 @@ g.test_ddl_handles_prepared = function(cg)
         end
     end)
 end
+
+-- The test covers the problem when already deleted space could be accessed
+-- by rollback of a deletion when another concurrent deletion was already
+-- prepared.
+-- Since the access is read-only and doesn't break anything, the test actually
+-- covers the problem only when ASAN is enabled.
+g.test_ddl_use_after_free_on_rollback_of_deletion = function(cg)
+    cg.server:exec(function()
+        local txn_proxy = require('test.box.lua.txn_proxy')
+        box.schema.space.create('test')
+        box.space.test:create_index('pk')
+        box.space.test:replace{1}
+
+        local tx = txn_proxy.new()
+        tx:begin()
+        tx('box.space.test:delete(1)')
+
+        box.space.test:delete(1)
+        box.space.test:create_index('sk')
+        tx:rollback()
+    end)
+end