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()
parent 557bb344
...@@ -253,6 +253,16 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index) ...@@ -253,6 +253,16 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index)
{ {
ut_ad(index->freed()); ut_ad(index->freed());
dict_table_t *table= index->table; 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(). */ /* Perform the skipped steps of dict_index_remove_from_cache_low(). */
UT_LIST_REMOVE(table->freed_indexes, index); UT_LIST_REMOVE(table->freed_indexes, index);
rw_lock_free(&index->lock); rw_lock_free(&index->lock);
...@@ -261,9 +271,15 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index) ...@@ -261,9 +271,15 @@ ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index)
if (!UT_LIST_GET_LEN(table->freed_indexes) && if (!UT_LIST_GET_LEN(table->freed_indexes) &&
!UT_LIST_GET_LEN(table->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); 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. */ /** Clear the adaptive hash index on all pages in the buffer pool. */
......
...@@ -1960,7 +1960,6 @@ dict_table_remove_from_cache_low( ...@@ -1960,7 +1960,6 @@ dict_table_remove_from_cache_low(
UT_DELETE(table->vc_templ); UT_DELETE(table->vc_templ);
} }
mysql_mutex_destroy(&table->autoinc_mutex);
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (UNIV_UNLIKELY(UT_LIST_GET_LEN(table->freed_indexes) != 0)) { if (UNIV_UNLIKELY(UT_LIST_GET_LEN(table->freed_indexes) != 0)) {
if (table->fts) { if (table->fts) {
...@@ -1975,6 +1974,7 @@ dict_table_remove_from_cache_low( ...@@ -1975,6 +1974,7 @@ dict_table_remove_from_cache_low(
} }
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
mysql_mutex_destroy(&table->autoinc_mutex);
dict_mem_table_free(table); dict_mem_table_free(table);
} }
......
...@@ -1912,7 +1912,6 @@ struct dict_table_t { ...@@ -1912,7 +1912,6 @@ struct dict_table_t {
determine whether we can evict the table from the dictionary cache. determine whether we can evict the table from the dictionary cache.
It is protected by lock_sys->mutex. */ It is protected by lock_sys->mutex. */
ulint n_rec_locks; ulint n_rec_locks;
private: private:
/** Count of how many handles are opened to this table. Dropping of the /** 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 table is NOT allowed until this count gets to zero. MySQL does NOT
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment