mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
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
This commit is contained in:
47
mysql-test/r/information_schema_columns.result
Normal file
47
mysql-test/r/information_schema_columns.result
Normal file
@ -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
|
||||
#
|
77
mysql-test/r/information_schema_tables.result
Normal file
77
mysql-test/r/information_schema_tables.result
Normal file
@ -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
|
||||
#
|
48
mysql-test/t/information_schema_columns.test
Normal file
48
mysql-test/t/information_schema_columns.test
Normal file
@ -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 #
|
95
mysql-test/t/information_schema_tables.test
Normal file
95
mysql-test/t/information_schema_tables.test
Normal file
@ -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 #
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user