summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2019-03-13 13:31:45 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2019-03-13 13:31:45 +0200
commit56304875f72d9aa051853bc736b6cf3fb9e346ad (patch)
treedcb6019fe3c43544223186de7094fd838568e502 /storage
parentec24dd0be9c8fe10c275f4f6870ec35ce5cbbd89 (diff)
downloadmariadb-git-56304875f72d9aa051853bc736b6cf3fb9e346ad.tar.gz
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.
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/dict/dict0mem.cc26
-rw-r--r--storage/innobase/handler/ha_innodb.cc16
-rw-r--r--storage/innobase/handler/ha_innodb.h4
-rw-r--r--storage/innobase/handler/handler0alter.cc4
-rw-r--r--storage/innobase/include/dict0mem.h11
-rw-r--r--storage/innobase/row/row0mysql.cc9
6 files changed, 41 insertions, 29 deletions
diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc
index b01cd657369..c9988997801 100644
--- a/storage/innobase/dict/dict0mem.cc
+++ b/storage/innobase/dict/dict0mem.cc
@@ -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);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 9bd85142e9f..33e0cc4c513 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -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;
}
}
diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h
index 1648d7db252..0162695c0e9 100644
--- a/storage/innobase/handler/ha_innodb.h
+++ b/storage/innobase/handler/ha_innodb.h
@@ -458,10 +458,6 @@ protected:
/** 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;
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 72acca5d967..49ed93aa195 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -6153,7 +6153,7 @@ err_exit:
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 @@ found_col:
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,
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 7fde68b3301..f59ff6fcd7f 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -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
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index cf40eb071e8..dcfc16ca42f 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -2590,10 +2590,15 @@ next:
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);