Skip to content

Commit

Permalink
MDEV-25243 ASAN heap-use-after-free in Item_func_sp::execute_impl upo…
Browse files Browse the repository at this point in the history
…n concurrent view DDL and I_S query with view and function
  • Loading branch information
abarkov committed Apr 21, 2022
1 parent 5ba7722 commit 2be617d
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 11 deletions.
47 changes: 47 additions & 0 deletions mysql-test/r/information_schema_columns.result
Original file line number Diff line number Diff line change
@@ -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 changes: 77 additions & 0 deletions mysql-test/r/information_schema_tables.result
Original file line number Diff line number Diff line change
@@ -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 changes: 48 additions & 0 deletions mysql-test/t/information_schema_columns.test
Original file line number Diff line number Diff line change
@@ -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 changes: 95 additions & 0 deletions mysql-test/t/information_schema_tables.test
Original file line number Diff line number Diff line change
@@ -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 #
12 changes: 12 additions & 0 deletions sql/sp_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
9 changes: 2 additions & 7 deletions sql/sql_class.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
}


Expand Down Expand Up @@ -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();

Expand Down
50 changes: 47 additions & 3 deletions sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -3089,8 +3135,6 @@ class THD :public Statement,
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;
Expand Down
Loading

0 comments on commit 2be617d

Please sign in to comment.