diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2019-03-13 13:31:45 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2019-03-13 13:31:45 +0200 |
commit | 56304875f72d9aa051853bc736b6cf3fb9e346ad (patch) | |
tree | dcb6019fe3c43544223186de7094fd838568e502 /storage | |
parent | ec24dd0be9c8fe10c275f4f6870ec35ce5cbbd89 (diff) | |
download | mariadb-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.cc | 26 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 16 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.h | 4 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 4 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 11 | ||||
-rw-r--r-- | storage/innobase/row/row0mysql.cc | 9 |
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); |