diff options
author | Alexander Barkov <bar@mariadb.com> | 2022-04-13 17:17:17 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2022-04-21 09:51:11 +0400 |
commit | 2be617d869e614e2c0e7313fd77649a03c891cfd (patch) | |
tree | d1f788014294c6f451f98a36937b8eea0f76e4d7 | |
parent | 5ba77222e9fe7af8ff403816b5338b18b342053c (diff) | |
download | mariadb-git-2be617d869e614e2c0e7313fd77649a03c891cfd.tar.gz |
MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function
-rw-r--r-- | mysql-test/r/information_schema_columns.result | 47 | ||||
-rw-r--r-- | mysql-test/r/information_schema_tables.result | 77 | ||||
-rw-r--r-- | mysql-test/t/information_schema_columns.test | 48 | ||||
-rw-r--r-- | mysql-test/t/information_schema_tables.test | 95 | ||||
-rw-r--r-- | sql/sp_cache.cc | 12 | ||||
-rw-r--r-- | sql/sql_class.cc | 9 | ||||
-rw-r--r-- | sql/sql_class.h | 50 | ||||
-rw-r--r-- | sql/sql_show.cc | 31 |
8 files changed, 358 insertions, 11 deletions
diff --git a/mysql-test/r/information_schema_columns.result b/mysql-test/r/information_schema_columns.result new file mode 100644 index 00000000000..3624a6db368 --- /dev/null +++ b/mysql-test/r/information_schema_columns.result @@ -0,0 +1,47 @@ +# +# Start of 10.2 tests +# +# +# MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +# +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); +connect con1,localhost,root,,; +SELECT GET_LOCK('v01',30); +GET_LOCK('v01',30) +1 +SELECT GET_LOCK('v02',30); +GET_LOCK('v02',30) +1 +connection default; +SELECT * FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) +AND RELEASE_LOCK(TABLE_NAME) +AND f1()=1 +ORDER BY TABLE_NAME; +connection con1; +connection con1; +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +RELEASE_LOCK('v01') +1 +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +RELEASE_LOCK('v02') +1 +DROP FUNCTION f2; +disconnect con1; +connection default; +SELECT RELEASE_LOCK('v01'); +RELEASE_LOCK('v01') +NULL +SELECT RELEASE_LOCK('v02'); +RELEASE_LOCK('v02') +NULL +DROP VIEW v01, v02; +DROP FUNCTION f1; +# +# End of 10.2 tests +# diff --git a/mysql-test/r/information_schema_tables.result b/mysql-test/r/information_schema_tables.result new file mode 100644 index 00000000000..33dafaa39eb --- /dev/null +++ b/mysql-test/r/information_schema_tables.result @@ -0,0 +1,77 @@ +# +# Start of 10.2 tests +# +# +# MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +# +# The originally reported non-deterministic test. +# It did not fail reliably on every run. +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); +CREATE FUNCTION f(b INT) RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f(SUM(a)) FROM t; +connect con1,localhost,root,,test; +LOOP +CREATE OR REPLACE VIEW vv AS SELECT 1; +END LOOP $ +connection default; +SELECT v.* FROM v JOIN INFORMATION_SCHEMA.TABLES WHERE DATA_LENGTH = -1; +f(SUM(a)) +KILL CONID; +disconnect con1; +connection default; +DROP VIEW IF EXISTS vv; +DROP VIEW v; +DROP FUNCTION f; +DROP TABLE t; +# The second test version from the MDEV. +# It failed more reliably, but still was not deterministic. +CREATE FUNCTION f() RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f() FROM seq_1_to_10; +SELECT * FROM INFORMATION_SCHEMA.TABLES, v;; +connect con1,localhost,root,,; +CREATE VIEW v2 AS SELECT 1; +connection default; +disconnect con1; +DROP VIEW v; +DROP VIEW v2; +DROP FUNCTION f; +# The third test version from the MDEV. +# It failed reliably, and should be deterninistic. +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); +connect con1,localhost,root,,; +SELECT GET_LOCK('v01',30); +GET_LOCK('v01',30) +1 +SELECT GET_LOCK('v02',30); +GET_LOCK('v02',30) +1 +connection default; +SELECT * FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) +AND RELEASE_LOCK(TABLE_NAME) +AND f1()=1 +ORDER BY TABLE_NAME; +connection con1; +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +RELEASE_LOCK('v01') +1 +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +RELEASE_LOCK('v02') +1 +DROP FUNCTION f2; +disconnect con1; +connection default; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +def test v01 VIEW NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL VIEW +def test v02 VIEW NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL VIEW +DROP VIEW v01, v02; +DROP FUNCTION f1; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/information_schema_columns.test b/mysql-test/t/information_schema_columns.test new file mode 100644 index 00000000000..0171b221110 --- /dev/null +++ b/mysql-test/t/information_schema_columns.test @@ -0,0 +1,48 @@ +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +--echo # + + +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); + +--connect(con1,localhost,root,,) +SELECT GET_LOCK('v01',30); +SELECT GET_LOCK('v02',30); +--connection default + +--send + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) + AND RELEASE_LOCK(TABLE_NAME) + AND f1()=1 + ORDER BY TABLE_NAME; + +--connection con1 +--connection con1 +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +DROP FUNCTION f2; +--disconnect con1 + +--connection default +--disable_result_log +--reap +--enable_result_log +SELECT RELEASE_LOCK('v01'); +SELECT RELEASE_LOCK('v02'); + +DROP VIEW v01, v02; +DROP FUNCTION f1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/mysql-test/t/information_schema_tables.test b/mysql-test/t/information_schema_tables.test new file mode 100644 index 00000000000..ebb1e21a65b --- /dev/null +++ b/mysql-test/t/information_schema_tables.test @@ -0,0 +1,95 @@ +--source include/have_sequence.inc + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upon concurrent view DDL and I_S query with view and function +--echo # + +--echo # The originally reported non-deterministic test. +--echo # It did not fail reliably on every run. + +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); +CREATE FUNCTION f(b INT) RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f(SUM(a)) FROM t; +--connect (con1,localhost,root,,test) +--let $conid= `SELECT CONNECTION_ID()` +--delimiter $ +--send +LOOP + CREATE OR REPLACE VIEW vv AS SELECT 1; +END LOOP $ +--delimiter ; +--connection default +--disable_warnings +SELECT v.* FROM v JOIN INFORMATION_SCHEMA.TABLES WHERE DATA_LENGTH = -1; +--enable_warnings +# Cleanup +--replace_result $conid CONID +--eval KILL $conid +--disconnect con1 +--connection default +DROP VIEW IF EXISTS vv; +DROP VIEW v; +DROP FUNCTION f; +DROP TABLE t; + + +--echo # The second test version from the MDEV. +--echo # It failed more reliably, but still was not deterministic. + + +CREATE FUNCTION f() RETURNS INT RETURN 1; +CREATE VIEW v AS SELECT f() FROM seq_1_to_10; +--send SELECT * FROM INFORMATION_SCHEMA.TABLES, v; +--connect(con1,localhost,root,,) +CREATE VIEW v2 AS SELECT 1; +--connection default +--disable_result_log +--reap +--enable_result_log +--disconnect con1 +DROP VIEW v; +DROP VIEW v2; +DROP FUNCTION f; + +--echo # The third test version from the MDEV. +--echo # It failed reliably, and should be deterninistic. + +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v01 AS SELECT f1(); +CREATE VIEW v02 AS SELECT f1(); + +--connect(con1,localhost,root,,) +SELECT GET_LOCK('v01',30); +SELECT GET_LOCK('v02',30); +--connection default + +--send + SELECT * FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA='test' + AND TABLE_NAME LIKE 'v0%' + AND GET_LOCK(TABLE_NAME,30) + AND RELEASE_LOCK(TABLE_NAME) + AND f1()=1 + ORDER BY TABLE_NAME; + +--connection con1 +SELECT RELEASE_LOCK('v01') /* Let the first row evaluate f1 */; +CREATE FUNCTION f2() RETURNS INT RETURN 1 /* Invalidate SP cache*/; +SELECT RELEASE_LOCK('v02') /* Let the second row evaluate f1() */; +DROP FUNCTION f2; +--disconnect con1 +--connection default +--reap + + +DROP VIEW v01, v02; +DROP FUNCTION f1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index bc91634eb32..9829167d29d 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -313,3 +313,15 @@ sp_cache::cleanup() { my_hash_free(&m_hashtable); } + + +void Sp_caches::sp_caches_clear() +{ + sp_cache_clear(&sp_proc_cache); + sp_cache_clear(&sp_func_cache); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + sp_cache_clear(&sp_package_spec_cache); + sp_cache_clear(&sp_package_body_cache); +#endif +} diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4edf573e596..aa64e42e144 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -806,9 +806,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC); - sp_proc_cache= NULL; - sp_func_cache= NULL; - /* For user vars replication*/ if (opt_bin_log) my_init_dynamic_array(&user_var_events, @@ -1353,8 +1350,7 @@ void THD::change_user(void) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, 0); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); + sp_caches_clear(); } @@ -1410,8 +1406,7 @@ void THD::cleanup(void) #endif /* defined(ENABLED_DEBUG_SYNC) */ my_hash_free(&user_vars); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); + sp_caches_clear(); auto_inc_intervals_forced.empty(); auto_inc_intervals_in_cur_stmt_for_binlog.empty(); diff --git a/sql/sql_class.h b/sql/sql_class.h index 54c9d4ac870..cceefb1793c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2116,6 +2116,51 @@ struct wait_for_commit }; +class Sp_caches +{ +public: + sp_cache *sp_proc_cache; + sp_cache *sp_func_cache; +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + sp_cache *sp_package_spec_cache; + sp_cache *sp_package_body_cache; +#endif + Sp_caches() + :sp_proc_cache(NULL), + sp_func_cache(NULL) +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + , + sp_package_spec_cache(NULL), + sp_package_body_cache(NULL) +#endif + { } + ~Sp_caches() + { + // All caches must be freed by the caller explicitly + DBUG_ASSERT(sp_proc_cache == NULL); + DBUG_ASSERT(sp_func_cache == NULL); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + DBUG_ASSERT(sp_package_spec_cache == NULL); + DBUG_ASSERT(sp_package_body_cache == NULL); +#endif + } + void sp_caches_swap(Sp_caches &rhs) + { + swap_variables(sp_cache*, sp_proc_cache, rhs.sp_proc_cache); + swap_variables(sp_cache*, sp_func_cache, rhs.sp_func_cache); +#if MYSQL_VERSION_ID >= 100300 +#error Remove the preprocessor condition, !!!but keep the code!!! + swap_variables(sp_cache*, sp_package_spec_cache, rhs.sp_package_spec_cache); + swap_variables(sp_cache*, sp_package_body_cache, rhs.sp_package_body_cache); +#endif + } + void sp_caches_clear(); +}; + + extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); class THD; @@ -2139,7 +2184,8 @@ class THD :public Statement, */ public Item_change_list, public MDL_context_owner, - public Open_tables_state + public Open_tables_state, + public Sp_caches { private: inline bool is_stmt_prepare() const @@ -3089,8 +3135,6 @@ public: int slave_expected_error; sp_rcontext *spcont; // SP runtime context - sp_cache *sp_proc_cache; - sp_cache *sp_func_cache; /** number of name_const() substitutions, see sp_head.cc:subst_spvars() */ uint query_name_consts; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c708849d049..ee295eb3834 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4907,6 +4907,7 @@ public: int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { + DBUG_ENTER("get_all_tables"); LEX *lex= thd->lex; TABLE *table= tables->table; TABLE_LIST table_acl_check; @@ -4923,7 +4924,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #endif uint table_open_method= tables->table_open_method; bool can_deadlock; - DBUG_ENTER("get_all_tables"); + /* + We're going to open FRM files for tables. + In case of VIEWs that contain stored function calls, + these stored functions will be parsed and put to the SP cache. + + Suppose we have a view containing a stored function call: + CREATE VIEW v1 AS SELECT f1() AS c1; + and now we're running: + SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=f1(); + If a parallel thread invalidates the cache, + e.g. by creating or dropping some stored routine, + the SELECT query will re-parse f1() when processing "v1" + and replace the outdated cached version of f1() to a new one. + But the old version of f1() is referenced from the m_sp member + of the Item_func_sp instances used in the WHERE condition. + We cannot destroy it. To avoid such clashes, let's remember + all old routines into a temporary SP cache collection + and process tables with a new empty temporary SP cache collection. + Then restore to the old SP cache collection at the end. + */ + Sp_caches old_sp_caches; + old_sp_caches.sp_caches_swap(*thd); /* In cases when SELECT from I_S table being filled by this call is @@ -5092,6 +5114,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) err: thd->restore_backup_open_tables_state(&open_tables_state_backup); + /* + Now restore to the saved SP cache collection + and clear the temporary SP cache collection. + */ + old_sp_caches.sp_caches_swap(*thd); + old_sp_caches.sp_caches_clear(); + DBUG_RETURN(error); } |