summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2021-08-19 16:46:01 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2021-08-21 12:38:10 +0530
commit08e5a3d2e3e0ee98a22f58adb9f76c686ba94a8a (patch)
treedb0188b34aa11f13d1aa52429d874c523c7737cc
parent557bb344e44d1fdba13325c1e3bfa8c775851f07 (diff)
downloadmariadb-git-08e5a3d2e3e0ee98a22f58adb9f76c686ba94a8a.tar.gz
MDEV-26383 ASAN heap-use-after-free failure in btr_search_lazy_free
Problem: ======= The last AHI page for two indexes of an dropped table is being freed at the same time by two threads. One thread frees the table heap and other thread tries to access table heap again. It leads to asan failure in btr_search_lazy_free(). Solution: ======== InnoDB uses autoinc_mutex to avoid the race condition in btr_search_lazy_free()
-rw-r--r--storage/innobase/btr/btr0sea.cc18
-rw-r--r--storage/innobase/dict/dict0dict.cc2
-rw-r--r--storage/innobase/include/dict0mem.h1
3 files changed, 18 insertions, 3 deletions
diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc
index 7b80d22c778..2e572fe32a8 100644
--- a/storage/innobase/btr/btr0sea.cc
+++ b/storage/innobase/btr/btr0sea.cc
@@ -253,6 +253,16 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index)
{
ut_ad(index->freed());
dict_table_t *table= index->table;
+ bool non_exist_table= (table->id == 0);
+
+ if (non_exist_table)
+ {
+ /* autoinc_mutex should be acquired to avoid the race condition
+ in case of multiple threads accessing the evicted table
+ or dropped table. */
+ mysql_mutex_lock(&table->autoinc_mutex);
+ }
+
/* Perform the skipped steps of dict_index_remove_from_cache_low(). */
UT_LIST_REMOVE(table->freed_indexes, index);
rw_lock_free(&index->lock);
@@ -261,9 +271,15 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index)
if (!UT_LIST_GET_LEN(table->freed_indexes) &&
!UT_LIST_GET_LEN(table->indexes))
{
- ut_ad(table->id == 0);
+ ut_ad(non_exist_table);
+ mysql_mutex_unlock(&table->autoinc_mutex);
+ mysql_mutex_destroy(&table->autoinc_mutex);
dict_mem_table_free(table);
+ return;
}
+
+ if (non_exist_table)
+ mysql_mutex_unlock(&table->autoinc_mutex);
}
/** Clear the adaptive hash index on all pages in the buffer pool. */
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 48c37855d3e..8243cf51526 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -1960,7 +1960,6 @@ dict_table_remove_from_cache_low(
UT_DELETE(table->vc_templ);
}
- mysql_mutex_destroy(&table->autoinc_mutex);
#ifdef BTR_CUR_HASH_ADAPT
if (UNIV_UNLIKELY(UT_LIST_GET_LEN(table->freed_indexes) != 0)) {
if (table->fts) {
@@ -1975,6 +1974,7 @@ dict_table_remove_from_cache_low(
}
#endif /* BTR_CUR_HASH_ADAPT */
+ mysql_mutex_destroy(&table->autoinc_mutex);
dict_mem_table_free(table);
}
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 5424b38a927..78244e2f6cb 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -1912,7 +1912,6 @@ struct dict_table_t {
determine whether we can evict the table from the dictionary cache.
It is protected by lock_sys->mutex. */
ulint n_rec_locks;
-
private:
/** Count of how many handles are opened to this table. Dropping of the
table is NOT allowed until this count gets to zero. MySQL does NOT