Skip to content

Commit

Permalink
MDEV-26979 heap-use-after-free or SIGSEGV when accessing INNODB_SYS_T…
Browse files Browse the repository at this point in the history
…ABLESTATS during DDL

i_s_dict_fill_sys_tablestats(): Read all fields of dict_table_t
while holding dict_sys.latch.

dict_sys_t::allow_eviction(): Remove.
  • Loading branch information
dr-m committed Jun 27, 2022
1 parent 20cf63f commit 1ae8160
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 77 deletions.
105 changes: 43 additions & 62 deletions storage/innobase/handler/i_s.cc
Expand Up @@ -5010,71 +5010,61 @@ static ST_FIELD_INFO innodb_sys_tablestats_fields_info[]=
};
} // namespace Show

/** Populate information_schema.innodb_sys_tablestats table with information
from SYS_TABLES.
@param[in] thd thread ID
@param[in,out] table table
@param[in] ref_count table reference count
@param[in,out] table_to_fill fill this table
/** Populate information_schema.innodb_sys_tablestats table with a table,
and release exclusive dict_sys.latch.
@param[in] thd connection
@param[in,out] table InnoDB table metadata
@param[in,out] table_to_fill INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS
@return 0 on success */
static
int
i_s_dict_fill_sys_tablestats(
THD* thd,
dict_table_t* table,
ulint ref_count,
TABLE* table_to_fill)
i_s_dict_fill_sys_tablestats(THD* thd, dict_table_t *table,
TABLE* table_to_fill)
{
Field** fields;

DBUG_ENTER("i_s_dict_fill_sys_tablestats");

fields = table_to_fill->field;

OK(fields[SYS_TABLESTATS_ID]->store(longlong(table->id), TRUE));
DBUG_ENTER("i_s_dict_fill_sys_tablestats");

OK(field_store_string(fields[SYS_TABLESTATS_NAME],
table->name.m_name));
Field **fields= table_to_fill->field;

{
table->stats_mutex_lock();
auto _ = make_scope_exit([table]() {
table->stats_mutex_unlock(); });

OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized,
true));

if (table->stat_initialized) {
OK(fields[SYS_TABLESTATS_NROW]->store(
table->stat_n_rows, true));
{
table->stats_mutex_lock();
auto _ = make_scope_exit([table]() {
table->stats_mutex_unlock(); dict_sys.unlock(); });

OK(fields[SYS_TABLESTATS_CLUST_SIZE]->store(
table->stat_clustered_index_size, true));
OK(fields[SYS_TABLESTATS_ID]->store(longlong(table->id), TRUE));

OK(fields[SYS_TABLESTATS_INDEX_SIZE]->store(
table->stat_sum_of_other_index_sizes,
true));
OK(field_store_string(fields[SYS_TABLESTATS_NAME],
table->name.m_name));
OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized, true));

OK(fields[SYS_TABLESTATS_MODIFIED]->store(
table->stat_modified_counter, true));
} else {
OK(fields[SYS_TABLESTATS_NROW]->store(0, true));
if (table->stat_initialized)
{
OK(fields[SYS_TABLESTATS_NROW]->store(table->stat_n_rows, true));

OK(fields[SYS_TABLESTATS_CLUST_SIZE]->store(0, true));
OK(fields[SYS_TABLESTATS_CLUST_SIZE]->
store(table->stat_clustered_index_size, true));

OK(fields[SYS_TABLESTATS_INDEX_SIZE]->store(0, true));
OK(fields[SYS_TABLESTATS_INDEX_SIZE]->
store(table->stat_sum_of_other_index_sizes, true));

OK(fields[SYS_TABLESTATS_MODIFIED]->store(0, true));
}
}

OK(fields[SYS_TABLESTATS_AUTONINC]->store(table->autoinc, true));
OK(fields[SYS_TABLESTATS_MODIFIED]->
store(table->stat_modified_counter, true));
}
else
{
OK(fields[SYS_TABLESTATS_NROW]->store(0, true));
OK(fields[SYS_TABLESTATS_CLUST_SIZE]->store(0, true));
OK(fields[SYS_TABLESTATS_INDEX_SIZE]->store(0, true));
OK(fields[SYS_TABLESTATS_MODIFIED]->store(0, true));
}

OK(fields[SYS_TABLESTATS_TABLE_REF_COUNT]->store(ref_count, true));
OK(fields[SYS_TABLESTATS_AUTONINC]->store(table->autoinc, true));

OK(schema_table_store_record(thd, table_to_fill));
OK(fields[SYS_TABLESTATS_TABLE_REF_COUNT]->
store(table->get_ref_count(), true));
}

DBUG_RETURN(0);
OK(schema_table_store_record(thd, table_to_fill));
DBUG_RETURN(0);
}

/*******************************************************************//**
Expand Down Expand Up @@ -5109,23 +5099,17 @@ i_s_sys_tables_fill_table_stats(

while (rec) {
const char* err_msg;
dict_table_t* table_rec= 0;
dict_table_t* table_rec = nullptr;

mtr.commit();
/* Fetch the dict_table_t structure corresponding to
this SYS_TABLES record */
err_msg = i_s_sys_tables_rec(pcur, nullptr, nullptr,
&table_rec);
&table_rec);

if (UNIV_LIKELY(!err_msg)) {
bool evictable = dict_sys.prevent_eviction(table_rec);
ulint ref_count = table_rec->get_ref_count();
dict_sys.unlock();
i_s_dict_fill_sys_tablestats(thd, table_rec, ref_count,
i_s_dict_fill_sys_tablestats(thd, table_rec,
tables->table);
if (!evictable) {
table_rec = nullptr;
}
} else {
ut_ad(!table_rec);
dict_sys.unlock();
Expand All @@ -5137,9 +5121,6 @@ i_s_sys_tables_fill_table_stats(
/* Get the next record */
mtr.start();
dict_sys.lock(SRW_LOCK_CALL);
if (table_rec) {
dict_sys.allow_eviction(table_rec);
}

rec = dict_getnext_system(&pcur, &mtr);
}
Expand Down
18 changes: 3 additions & 15 deletions storage/innobase/include/dict0dict.h
Expand Up @@ -1487,28 +1487,16 @@ class dict_sys_t
}
#endif

/** Move a table to the non-LRU list from the LRU list.
@return whether the table was evictable */
bool prevent_eviction(dict_table_t *table)
/** Move a table to the non-LRU list from the LRU list. */
void prevent_eviction(dict_table_t *table)
{
ut_d(locked());
ut_ad(find(table));
if (!table->can_be_evicted)
return false;
return;
table->can_be_evicted= false;
UT_LIST_REMOVE(table_LRU, table);
UT_LIST_ADD_LAST(table_non_LRU, table);
return true;
}
/** Move a table from the non-LRU list to the LRU list. */
void allow_eviction(dict_table_t *table)
{
ut_d(locked());
ut_ad(find(table));
ut_ad(!table->can_be_evicted);
table->can_be_evicted= true;
UT_LIST_REMOVE(table_non_LRU, table);
UT_LIST_ADD_FIRST(table_LRU, table);
}

#ifdef UNIV_DEBUG
Expand Down

0 comments on commit 1ae8160

Please sign in to comment.