diff options
author | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2021-08-19 16:46:01 +0530 |
---|---|---|
committer | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2021-08-21 12:38:10 +0530 |
commit | 08e5a3d2e3e0ee98a22f58adb9f76c686ba94a8a (patch) | |
tree | db0188b34aa11f13d1aa52429d874c523c7737cc | |
parent | 557bb344e44d1fdba13325c1e3bfa8c775851f07 (diff) | |
download | mariadb-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.cc | 18 | ||||
-rw-r--r-- | storage/innobase/dict/dict0dict.cc | 2 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 1 |
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 |