diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2019-05-13 17:16:42 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2019-05-13 17:16:42 +0300 |
commit | 2647fd101db922b1e6c7363124adfc03e81ab8a0 (patch) | |
tree | 6287fb64d502c6e3dfd891ba56c80f8bc552756f /storage/innobase | |
parent | 1c97e07f8f30a968521054d658b8075f2ab2a8d2 (diff) | |
download | mariadb-git-2647fd101db922b1e6c7363124adfc03e81ab8a0.tar.gz |
MDEV-19445 heap-use-after-free related to innodb_ft_aux_table
Try to fix the race conditions between
SET GLOBAL innodb_ft_aux_table = ...;
and access to the INFORMATION_SCHEMA tables that depend on
this variable.
innodb_ft_aux_table: Replaces
fts_internal_tbl_name,fts_internal_tbl_name2. Just store the
user-specified parameter as is.
innodb_ft_aux_table_id: The table_id corresponding to
SET GLOBAL innodb_ft_aux_table, or 0 if the table does not exist
or does not contain FULLTEXT INDEX. If the table is renamed later,
the INFORMATION_SCHEMA tables will continue to refer to the table.
If the table is dropped or rebuilt, the INFORMATION_SCHEMA tables
will not find the table.
Diffstat (limited to 'storage/innobase')
-rw-r--r-- | storage/innobase/fts/fts0fts.cc | 5 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 123 | ||||
-rw-r--r-- | storage/innobase/handler/i_s.cc | 103 | ||||
-rw-r--r-- | storage/innobase/handler/i_s.h | 6 | ||||
-rw-r--r-- | storage/innobase/include/fts0fts.h | 5 |
5 files changed, 83 insertions, 159 deletions
diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index bfb469b1e2d..beed1b99d6c 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -107,11 +107,6 @@ UNIV_INTERN mysql_pfs_key_t fts_doc_id_mutex_key; UNIV_INTERN mysql_pfs_key_t fts_pll_tokenize_mutex_key; #endif /* UNIV_PFS_MUTEX */ -/** variable to record innodb_fts_internal_tbl_name for information -schema table INNODB_FTS_INSERTED etc. */ -UNIV_INTERN char* fts_internal_tbl_name = NULL; -UNIV_INTERN char* fts_internal_tbl_name2 = NULL; - /** InnoDB default stopword list: There are different versions of stopwords, the stop words listed below comes from "Google Stopword" list. Reference: diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 8b007bb0b38..cb6ec20e0d6 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -17118,98 +17118,36 @@ innodb_stopword_table_validate( return(ret); } -/*************************************************************//** -Check whether valid argument given to "innodb_fts_internal_tbl_name" -This function is registered as a callback with MySQL. -@return 0 for valid stopword table */ -static -int -innodb_internal_table_validate( -/*===========================*/ - THD* thd, /*!< in: thread handle */ - struct st_mysql_sys_var* var, /*!< in: pointer to system - variable */ - void* save, /*!< out: immediate result - for update function */ - struct st_mysql_value* value) /*!< in: incoming string */ -{ - const char* table_name; - char buff[STRING_BUFFER_USUAL_SIZE]; - int len = sizeof(buff); - int ret = 1; - dict_table_t* user_table; - - ut_a(save != NULL); - ut_a(value != NULL); - - table_name = value->val_str(value, buff, &len); - - if (!table_name) { - *static_cast<const char**>(save) = NULL; - return(0); - } - - user_table = dict_table_open_on_name( - table_name, FALSE, TRUE, DICT_ERR_IGNORE_NONE); - - if (user_table) { - if (dict_table_has_fts_index(user_table)) { - *static_cast<const char**>(save) = table_name; - ret = 0; +/** The latest assigned innodb_ft_aux_table name */ +static char* innodb_ft_aux_table; + +/** Update innodb_ft_aux_table_id on SET GLOBAL innodb_ft_aux_table. +@param[out] save new value of innodb_ft_aux_table +@param[in] value user-specified value */ +static int innodb_ft_aux_table_validate(THD*, st_mysql_sys_var*, + void* save, st_mysql_value* value) +{ + char buf[STRING_BUFFER_USUAL_SIZE]; + int len = sizeof buf; + + if (const char* table_name = value->val_str(value, buf, &len)) { + if (dict_table_t* table = dict_table_open_on_name( + table_name, FALSE, TRUE, DICT_ERR_IGNORE_NONE)) { + const table_id_t id = dict_table_has_fts_index(table) + ? table->id : 0; + dict_table_close(table, FALSE, FALSE); + if (id) { + innodb_ft_aux_table_id = id; + *static_cast<const char**>(save) = table_name; + return 0; + } } - dict_table_close(user_table, FALSE, TRUE); - - DBUG_EXECUTE_IF("innodb_evict_autoinc_table", - mutex_enter(&dict_sys->mutex); - dict_table_remove_from_cache_low(user_table, TRUE); - mutex_exit(&dict_sys->mutex); - ); - } - - return(ret); -} - -/****************************************************************//** -Update global variable "fts_internal_tbl_name" with the "saved" -stopword table name value. This function is registered as a callback -with MySQL. */ -static -void -innodb_internal_table_update( -/*=========================*/ - THD* thd, /*!< in: thread handle */ - struct st_mysql_sys_var* var, /*!< in: pointer to - system variable */ - void* var_ptr,/*!< out: where the - formal string goes */ - const void* save) /*!< in: immediate result - from check function */ -{ - const char* table_name; - char* old; - - ut_a(save != NULL); - ut_a(var_ptr != NULL); - - table_name = *static_cast<const char*const*>(save); - old = *(char**) var_ptr; - - if (table_name) { - *(char**) var_ptr = my_strdup(table_name, MYF(0)); - } else { - *(char**) var_ptr = NULL; - } - - if (old) { - my_free(old); - } - - fts_internal_tbl_name2 = *(char**) var_ptr; - if (fts_internal_tbl_name2 == NULL) { - fts_internal_tbl_name = const_cast<char*>("default"); + return 1; } else { - fts_internal_tbl_name = fts_internal_tbl_name2; + *static_cast<char**>(save) = NULL; + innodb_ft_aux_table_id = 0; + return 0; } } @@ -19476,11 +19414,10 @@ static MYSQL_SYSVAR_BOOL(disable_sort_file_cache, srv_disable_sort_file_cache, "Whether to disable OS system file cache for sort I/O", NULL, NULL, FALSE); -static MYSQL_SYSVAR_STR(ft_aux_table, fts_internal_tbl_name2, - PLUGIN_VAR_NOCMDARG, +static MYSQL_SYSVAR_STR(ft_aux_table, innodb_ft_aux_table, + PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_MEMALLOC, "FTS internal auxiliary table to be checked", - innodb_internal_table_validate, - innodb_internal_table_update, NULL); + innodb_ft_aux_table_validate, NULL, NULL); static MYSQL_SYSVAR_ULONG(ft_cache_size, fts_max_cache_size, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 9eae8ded633..2729a755570 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -64,6 +64,9 @@ Modified Dec 29, 2014 Jan Lindström (Added sys_semaphore_waits) #include "fil0fil.h" #include "fil0crypt.h" +/** The latest successfully looked up innodb_fts_aux_table */ +UNIV_INTERN table_id_t innodb_ft_aux_table_id; + /** structure associates a name string with a file page type and/or buffer page state. */ struct buf_page_desc_t{ @@ -2934,25 +2937,21 @@ i_s_fts_deleted_generic_fill( DBUG_RETURN(0); } - if (!fts_internal_tbl_name) { - DBUG_RETURN(0); - } + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name); - /* Prevent DDL to drop fts aux tables. */ + /* Prevent DROP of the internal tables for fulltext indexes. + FIXME: acquire DDL-blocking MDL on the user table name! */ rw_lock_s_lock(&dict_operation_lock); - user_table = dict_table_open_on_name( - fts_internal_tbl_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE); + user_table = dict_table_open_on_id( + innodb_ft_aux_table_id, FALSE, DICT_TABLE_OP_NORMAL); if (!user_table) { rw_lock_s_unlock(&dict_operation_lock); - DBUG_RETURN(0); } else if (!dict_table_has_fts_index(user_table)) { dict_table_close(user_table, FALSE, FALSE); - rw_lock_s_unlock(&dict_operation_lock); - DBUG_RETURN(0); } @@ -2967,6 +2966,12 @@ i_s_fts_deleted_generic_fill( fts_table_fetch_doc_ids(trx, &fts_table, deleted); + dict_table_close(user_table, FALSE, FALSE); + + rw_lock_s_unlock(&dict_operation_lock); + + trx_free_for_background(trx); + fields = table->field; int ret = 0; @@ -2981,14 +2986,8 @@ i_s_fts_deleted_generic_fill( BREAK_IF(ret = schema_table_store_record(thd, table)); } - trx_free_for_background(trx); - fts_doc_ids_free(deleted); - dict_table_close(user_table, FALSE, FALSE); - - rw_lock_s_unlock(&dict_operation_lock); - DBUG_RETURN(ret); } @@ -3348,32 +3347,33 @@ i_s_fts_index_cache_fill( DBUG_RETURN(0); } - if (!fts_internal_tbl_name) { - DBUG_RETURN(0); - } + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name); - user_table = dict_table_open_on_name( - fts_internal_tbl_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE); + /* Prevent DROP of the internal tables for fulltext indexes. + FIXME: acquire DDL-blocking MDL on the user table name! */ + rw_lock_s_lock(&dict_operation_lock); + + user_table = dict_table_open_on_id( + innodb_ft_aux_table_id, FALSE, DICT_TABLE_OP_NORMAL); if (!user_table) { +no_fts: + rw_lock_s_unlock(&dict_operation_lock); DBUG_RETURN(0); } - if (user_table->fts == NULL || user_table->fts->cache == NULL) { + if (!user_table->fts || !user_table->fts->cache) { dict_table_close(user_table, FALSE, FALSE); - - DBUG_RETURN(0); + goto no_fts; } cache = user_table->fts->cache; - ut_a(cache); - int ret = 0; fts_string_t conv_str; - conv_str.f_len = system_charset_info->mbmaxlen - * FTS_MAX_WORD_LEN_IN_CHAR; - conv_str.f_str = static_cast<byte*>(ut_malloc(conv_str.f_len)); + byte word[HA_FT_MAXBYTELEN + 1]; + conv_str.f_len = sizeof word; + conv_str.f_str = word; for (ulint i = 0; i < ib_vector_size(cache->indexes); i++) { fts_index_cache_t* index_cache; @@ -3385,9 +3385,8 @@ i_s_fts_index_cache_fill( index_cache, thd, &conv_str, tables)); } - ut_free(conv_str.f_str); - dict_table_close(user_table, FALSE, FALSE); + rw_lock_s_unlock(&dict_operation_lock); DBUG_RETURN(ret); } @@ -3801,19 +3800,17 @@ i_s_fts_index_table_fill( DBUG_RETURN(0); } - if (!fts_internal_tbl_name) { - DBUG_RETURN(0); - } + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name); - /* Prevent DDL to drop fts aux tables. */ + /* Prevent DROP of the internal tables for fulltext indexes. + FIXME: acquire DDL-blocking MDL on the user table name! */ rw_lock_s_lock(&dict_operation_lock); - user_table = dict_table_open_on_name( - fts_internal_tbl_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE); + user_table = dict_table_open_on_id( + innodb_ft_aux_table_id, FALSE, DICT_TABLE_OP_NORMAL); if (!user_table) { rw_lock_s_unlock(&dict_operation_lock); - DBUG_RETURN(0); } @@ -3966,32 +3963,28 @@ i_s_fts_config_fill( DBUG_RETURN(0); } - if (!fts_internal_tbl_name) { - DBUG_RETURN(0); - } - - DEBUG_SYNC_C("i_s_fts_config_fille_check"); - - fields = table->field; + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name); - /* Prevent DDL to drop fts aux tables. */ + /* Prevent DROP of the internal tables for fulltext indexes. + FIXME: acquire DDL-blocking MDL on the user table name! */ rw_lock_s_lock(&dict_operation_lock); - user_table = dict_table_open_on_name( - fts_internal_tbl_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE); + user_table = dict_table_open_on_id( + innodb_ft_aux_table_id, FALSE, DICT_TABLE_OP_NORMAL); if (!user_table) { +no_fts: rw_lock_s_unlock(&dict_operation_lock); - DBUG_RETURN(0); - } else if (!dict_table_has_fts_index(user_table)) { - dict_table_close(user_table, FALSE, FALSE); - - rw_lock_s_unlock(&dict_operation_lock); + } - DBUG_RETURN(0); + if (!dict_table_has_fts_index(user_table)) { + dict_table_close(user_table, FALSE, FALSE); + goto no_fts; } + fields = table->field; + trx = trx_allocate_for_background(); trx->op_info = "Select for FTS CONFIG TABLE"; @@ -4043,12 +4036,12 @@ i_s_fts_config_fill( fts_sql_commit(trx); - trx_free_for_background(trx); - dict_table_close(user_table, FALSE, FALSE); rw_lock_s_unlock(&dict_operation_lock); + trx_free_for_background(trx); + DBUG_RETURN(ret); } diff --git a/storage/innobase/handler/i_s.h b/storage/innobase/handler/i_s.h index 596774a0b50..9dc025fa649 100644 --- a/storage/innobase/handler/i_s.h +++ b/storage/innobase/handler/i_s.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved. -Copyrigth (c) 2014, 2017, MariaDB Corporation. +Copyrigth (c) 2014, 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 @@ -27,6 +27,7 @@ Modified Dec 29, 2014 Jan Lindström #ifndef i_s_h #define i_s_h +#include "dict0types.h" const char plugin_author[] = "Oracle Corporation"; const char maria_plugin_author[] = "MariaDB Corporation"; @@ -64,6 +65,9 @@ extern struct st_maria_plugin i_s_innodb_tablespaces_encryption; extern struct st_maria_plugin i_s_innodb_tablespaces_scrubbing; extern struct st_maria_plugin i_s_innodb_sys_semaphore_waits; +/** The latest successfully looked up innodb_fts_aux_table */ +extern table_id_t innodb_ft_aux_table_id; + /** maximum number of buffer page info we would cache. */ #define MAX_BUF_INFO_CACHED 10000 diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index 19a1058dc0c..a1fab659732 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -367,11 +367,6 @@ extern bool fts_need_sync; /** Maximum possible Fulltext word length (in characters) */ #define FTS_MAX_WORD_LEN_IN_CHAR HA_FT_MAXCHARLEN -/** Variable specifying the table that has Fulltext index to display its -content through information schema table */ -extern char* fts_internal_tbl_name; -extern char* fts_internal_tbl_name2; - #define fts_que_graph_free(graph) \ do { \ mutex_enter(&dict_sys->mutex); \ |