Commit 56304875 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18836 ASAN: heap-use-after-free after TRUNCATE

row_drop_tables_for_mysql_in_background(): Copy the table name
before closing the table handle, to avoid heap-use-after-free if
another thread succeeds in dropping the table before
row_drop_table_for_mysql_in_background() completes the table name lookup.

dict_mem_create_temporary_tablename(): With innodb_safe_truncate=ON
(the default), generate a simple, unique, collision-free table name
using only the id, no pseudorandom component. This is safe, because
on startup, we will drop any #sql tables that might exist in InnoDB.
This is a backport from 10.3. It should have been backported already
as part of backporting MDEV-14717,MDEV-14585 which were prerequisites
for the MDEV-13564 backup-friendly TRUNCATE TABLE.
This seems to reduce the chance of table creation failures in
ha_innobase::truncate().

ha_innobase::truncate(): Do not invoke close(), but instead
mimic it, so that we can restore to the original table handle
in case opening the truncated copy of the table failed.
parent ec24dd0b
......@@ -2,7 +2,7 @@
Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2013, 2018, MariaDB Corporation.
Copyright (c) 2013, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -1044,14 +1044,7 @@ dict_mem_index_free(
mem_heap_free(index->heap);
}
/** Create a temporary tablename like "#sql-ibtid-inc where
tid = the Table ID
inc = a randomly initialized number that is incremented for each file
The table ID is a 64 bit integer, can use up to 20 digits, and is
initialized at bootstrap. The second number is 32 bits, can use up to 10
digits, and is initialized at startup to a randomly distributed number.
It is hoped that the combination of these two numbers will provide a
reasonably unique temporary file name.
/** Create a temporary tablename like "#sql-ibNNN".
@param[in] heap A memory heap
@param[in] dbtab Table name in the form database/table name
@param[in] id Table id
......@@ -1066,8 +1059,19 @@ dict_mem_create_temporary_tablename(
char* name;
const char* dbend = strchr(dbtab, '/');
ut_ad(dbend);
size_t dblen = dbend - dbtab + 1;
size_t dblen = size_t(dbend - dbtab) + 1;
if (srv_safe_truncate) {
/* InnoDB will drop all #sql tables at startup.
Therefore, the id alone should generate a unique
and previously non-existent name. */
size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20);
name = static_cast<char*>(mem_heap_alloc(heap, size));
memcpy(name, dbtab, dblen);
snprintf(name + dblen, size - dblen,
TEMP_FILE_PREFIX_INNODB UINT64PF, id);
return name;
}
/* Increment a randomly initialized number for each temp file. */
my_atomic_add32((int32*) &dict_temp_file_num, 1);
......
......@@ -13623,12 +13623,26 @@ int ha_innobase::truncate()
if (!err) {
/* Reopen the newly created table, and drop the
original table that was renamed to temp_name. */
close();
row_prebuilt_t* prebuilt = m_prebuilt;
uchar* upd_buf = m_upd_buf;
ulint upd_buf_size = m_upd_buf_size;
/* Mimic ha_innobase::close(). */
m_prebuilt = NULL;
m_upd_buf = NULL;
m_upd_buf_size = 0;
err = open(name, 0, 0);
if (!err) {
m_prebuilt->stored_select_lock_type = stored_lock;
m_prebuilt->table->update_time = update_time;
row_prebuilt_free(prebuilt, FALSE);
delete_table(temp_name, SQLCOM_TRUNCATE);
my_free(upd_buf);
} else {
/* Revert to the old table before truncation. */
m_prebuilt = prebuilt;
m_upd_buf = upd_buf;
m_upd_buf_size = upd_buf_size;
}
}
......
......@@ -458,10 +458,6 @@ class ha_innobase: public handler
/** Save CPU time with prebuilt/cached data structures */
row_prebuilt_t* m_prebuilt;
/** prebuilt pointer for the right prebuilt. For native
partitioning, points to the current partition prebuilt. */
row_prebuilt_t** m_prebuilt_ptr;
/** Thread handle of the user currently using the handler;
this is set in external_lock function */
THD* m_user_thd;
......
......@@ -6153,7 +6153,7 @@ ha_innobase::prepare_inplace_alter_table(
if (heap) {
ha_alter_info->handler_ctx
= new ha_innobase_inplace_ctx(
(*m_prebuilt_ptr),
m_prebuilt,
drop_index, n_drop_index,
rename_index, n_rename_index,
drop_fk, n_drop_fk,
......@@ -6287,7 +6287,7 @@ ha_innobase::prepare_inplace_alter_table(
DBUG_ASSERT(!ha_alter_info->handler_ctx);
ha_alter_info->handler_ctx = new ha_innobase_inplace_ctx(
(*m_prebuilt_ptr),
m_prebuilt,
drop_index, n_drop_index,
rename_index, n_rename_index,
drop_fk, n_drop_fk, add_fk, n_add_fk,
......
......@@ -2,7 +2,7 @@
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2013, 2018, MariaDB Corporation.
Copyright (c) 2013, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -495,14 +495,7 @@ void
dict_mem_table_free_foreign_vcol_set(
dict_table_t* table);
/** Create a temporary tablename like "#sql-ibtid-inc where
tid = the Table ID
inc = a randomly initialized number that is incremented for each file
The table ID is a 64 bit integer, can use up to 20 digits, and is
initialized at bootstrap. The second number is 32 bits, can use up to 10
digits, and is initialized at startup to a randomly distributed number.
It is hoped that the combination of these two numbers will provide a
reasonably unique temporary file name.
/** Create a temporary tablename like "#sql-ibNNN".
@param[in] heap A memory heap
@param[in] dbtab Table name in the form database/table name
@param[in] id Table id
......
......@@ -2590,10 +2590,15 @@ row_drop_tables_for_mysql_in_background(void)
goto next;
}
char* name = mem_strdup(table->name.m_name);
dict_table_close(table, FALSE, FALSE);
if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
table->name.m_name)) {
dberr_t err = row_drop_table_for_mysql_in_background(name);
ut_free(name);
if (err != DB_SUCCESS) {
/* If the DROP fails for some table, we return, and let the
main thread retry later */
return(n_tables + n_tables_dropped);
......
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