Commit 132270d3 authored by Yuchen Pei's avatar Yuchen Pei

MDEV-34541 Clean up spider self reference check

SPIDER_CONN::loop_check_meraged_first is useless, because all
SPIDER_CONN_LOOP_CHECKs are in SPIDER_CONN::loop_check_queue, which in
spider_db_conn::fin_loop_check() is iterated over.

This fixes the use-after-free issue when there are three spider tables
sharing the same remote, and their corresponding
SPIDER_CONN_LOOP_CHECKs getting merged in
spider_conn_queue_and_merge_loop_check()

This also fixes MDEV-34555
parent 0bb98628
for master_1
for child2
for child3
SET SESSION spider_same_server_link=1;
SET sql_mode='';
set @old_table_open_cache=@@global.table_open_cache;
set global table_open_cache=10;
set spider_same_server_link= 1;
CREATE SERVER srv FOREIGN DATA WRAPPER mysql
OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
CREATE TABLE t1 (c INT) ENGINE=InnoDB;
CREATE TABLE t2 (c INT) ENGINE=InnoDB;
CREATE TABLE t3 (c INT) ENGINE=InnoDB;
CREATE TABLE ta (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t5 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t6 (c INT KEY) ENGINE=InnoDB PARTITION BY RANGE (c) (PARTITION p VALUES LESS THAN (5));
CREATE TABLE t7 (a INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t8 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
SELECT * FROM t8;
ERROR HY000: Remote table 'test.t' is not found
CREATE TEMPORARY TABLE t7 (c INT) ENGINE=InnoDB SELECT * FROM t7;
ERROR HY000: Remote table 'test.t' is not found
CALL foo;
ERROR 42000: PROCEDURE test.foo does not exist
CREATE TEMPORARY TABLE t7 (c INT) ENGINE=InnoDB;
SELECT * FROM t7 JOIN t6 ON tc=t0.c;
ERROR 42S22: Unknown column 'tc' in 'on clause'
SHOW TABLE STATUS;
drop table ta, t8, t7, t6, t5, t3, t2, t1;
drop table t7;
drop server srv;
set global table_open_cache=@old_table_open_cache;
for master_1
for child2
for child3
for master_1
for child2
for child3
set spider_same_server_link= 1;
CREATE SERVER srv FOREIGN DATA WRAPPER mysql
OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE '',user 'Spider', password 'foo');
CREATE TABLE tSpider (a INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t2 (c INT,c2 CHAR(1)) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
XA START 'a';
SELECT * FROM information_schema.table_constraints;
SELECT SLEEP (1);
SLEEP (1)
0
SELECT * FROM t2;
ERROR HY000: Unable to connect to foreign data source: srv
SELECT SLEEP (1);
SLEEP (1)
0
SELECT * FROM t2;
ERROR HY000: Unable to connect to foreign data source: srv
SELECT SLEEP (1);
SLEEP (1)
0
SELECT * FROM t2;
ERROR HY000: Unable to connect to foreign data source: srv
xa end 'a';
xa rollback 'a';
drop table tSpider, t2;
drop server srv;
for master_1
for child2
for child3
--disable_query_log
--disable_result_log
--source ../../t/test_init.inc
--enable_result_log
--enable_query_log
SET SESSION spider_same_server_link=1;
SET sql_mode='';
set @old_table_open_cache=@@global.table_open_cache;
set global table_open_cache=10;
set spider_same_server_link= 1;
evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql
OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root');
CREATE TABLE t1 (c INT) ENGINE=InnoDB;
CREATE TABLE t2 (c INT) ENGINE=InnoDB;
CREATE TABLE t3 (c INT) ENGINE=InnoDB;
CREATE TABLE ta (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t5 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t6 (c INT KEY) ENGINE=InnoDB PARTITION BY RANGE (c) (PARTITION p VALUES LESS THAN (5));
CREATE TABLE t7 (a INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t8 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
--error 12702
SELECT * FROM t8;
--error 12702
CREATE TEMPORARY TABLE t7 (c INT) ENGINE=InnoDB SELECT * FROM t7;
--error ER_SP_DOES_NOT_EXIST
CALL foo;
CREATE TEMPORARY TABLE t7 (c INT) ENGINE=InnoDB;
--error ER_BAD_FIELD_ERROR
SELECT * FROM t7 JOIN t6 ON tc=t0.c;
--disable_result_log
SHOW TABLE STATUS;
--enable_result_log
# we need to drop t7 twice
drop table ta, t8, t7, t6, t5, t3, t2, t1;
drop table t7;
drop server srv;
set global table_open_cache=@old_table_open_cache;
--disable_query_log
--disable_result_log
--source ../../t/test_deinit.inc
--enable_result_log
--enable_query_log
--disable_query_log
--disable_result_log
--source ../../t/test_init.inc
--enable_result_log
--enable_query_log
set spider_same_server_link= 1;
evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql
OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE '',user 'Spider', password 'foo');
CREATE TABLE tSpider (a INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
CREATE TABLE t2 (c INT,c2 CHAR(1)) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE "t"';
XA START 'a';
--disable_result_log
--error 0,ER_CONNECT_TO_FOREIGN_DATA_SOURCE
SELECT * FROM information_schema.table_constraints;
--enable_result_log
SELECT SLEEP (1);
--error ER_CONNECT_TO_FOREIGN_DATA_SOURCE
SELECT * FROM t2;
SELECT SLEEP (1);
--error ER_CONNECT_TO_FOREIGN_DATA_SOURCE
SELECT * FROM t2;
SELECT SLEEP (1);
--error ER_CONNECT_TO_FOREIGN_DATA_SOURCE
SELECT * FROM t2;
xa end 'a';
xa rollback 'a';
drop table tSpider, t2;
drop server srv;
--disable_query_log
--disable_result_log
--source ../../t/test_deinit.inc
--enable_result_log
--enable_query_log
......@@ -1151,27 +1151,41 @@ void spider_conn_queue_UTC_time_zone(
DBUG_VOID_RETURN;
}
/*
Construct merged values and insert into the loop check queue
Search the loop_check_queue for the data node table, and if one does
not exist, construct the merged value in the same format as the
right hand side. Otherwise, merge the right hand side of the
existing SPIDER_CONN_LOOP_CHECK with the right hand side of lcptr
into one right hand side. In either case, add the
SPIDER_CONN_LOOP_CHECK to the loop check queue
*/
int spider_conn_queue_and_merge_loop_check(
SPIDER_CONN *conn,
SPIDER_CONN_LOOP_CHECK *lcptr
) {
int error_num = HA_ERR_OUT_OF_MEM;
char *tmp_name, *from_name, *cur_name, *to_name, *full_name, *from_value,
char *tmp_name, *cur_name, *to_name, *full_name, *from_value,
*merged_value;
SPIDER_CONN_LOOP_CHECK *lcqptr, *lcrptr;
DBUG_ENTER("spider_conn_queue_and_merge_loop_check");
DBUG_PRINT("info", ("spider conn=%p", conn));
#ifdef SPIDER_HAS_HASH_VALUE_TYPE
if (unlikely(!(lcqptr = (SPIDER_CONN_LOOP_CHECK *)
if (!(lcqptr = (SPIDER_CONN_LOOP_CHECK *)
my_hash_search_using_hash_value(&conn->loop_check_queue,
lcptr->hash_value_to,
(uchar *) lcptr->to_name.str, lcptr->to_name.length))))
(uchar *) lcptr->to_name.str, lcptr->to_name.length)))
#else
if (unlikely(!(lcqptr = (SPIDER_CONN_LOOP_CHECK *) my_hash_search(
&conn->loop_check_queue,
(uchar *) lcptr->to_name.str, lcptr->to_name.length))))
#endif
{
/*
Construct the right hand side:
-<mac>-<pid>-<cur_table>-<from_value>
*/
DBUG_PRINT("info", ("spider create merged_value and insert"));
lcptr->merged_value.length = spider_unique_id.length +
lcptr->cur_name.length + lcptr->from_value.length + 1;
......@@ -1194,10 +1208,10 @@ int spider_conn_queue_and_merge_loop_check(
}
lcptr->flag |= SPIDER_LOP_CHK_QUEUED;
} else {
/* Merge lcptr and lcqptr into a newly created lcrptr. */
DBUG_PRINT("info", ("spider append merged_value and replace"));
if (unlikely(!spider_bulk_malloc(spider_current_trx, 271, MYF(MY_WME),
&lcrptr, (uint) (sizeof(SPIDER_CONN_LOOP_CHECK)),
&from_name, (uint) (lcqptr->from_name.length + 1),
&cur_name, (uint) (lcqptr->cur_name.length + 1),
&to_name, (uint) (lcqptr->to_name.length + 1),
&full_name, (uint) (lcqptr->full_name.length + 1),
......@@ -1209,13 +1223,13 @@ int spider_conn_queue_and_merge_loop_check(
)) {
goto error_alloc_loop_check_replace;
}
/*
TODO: the new lcrptr has the same cur_name, to_name, full_name
and from_value as lcqptr, but they do not seem to be relevant.
*/
#ifdef SPIDER_HAS_HASH_VALUE_TYPE
lcrptr->hash_value_to = lcqptr->hash_value_to;
lcrptr->hash_value_full = lcqptr->hash_value_full;
#endif
lcrptr->from_name.str = from_name;
lcrptr->from_name.length = lcqptr->from_name.length;
memcpy(from_name, lcqptr->from_name.str, lcqptr->from_name.length + 1);
lcrptr->cur_name.str = cur_name;
lcrptr->cur_name.length = lcqptr->cur_name.length;
memcpy(cur_name, lcqptr->cur_name.str, lcqptr->cur_name.length + 1);
......@@ -1228,8 +1242,14 @@ int spider_conn_queue_and_merge_loop_check(
lcrptr->from_value.str = from_value;
lcrptr->from_value.length = lcqptr->from_value.length;
memcpy(from_value, lcqptr->from_value.str, lcqptr->from_value.length + 1);
/*
The merged_value of lcrptr is a concatenation of that of lcqptr
and constructed merged_value from lcptr.
*/
lcrptr->merged_value.str = merged_value;
lcrptr->merged_value.length = lcqptr->merged_value.length;
lcrptr->merged_value.length =
lcqptr->merged_value.length + spider_unique_id.length +
lcptr->cur_name.length + 1 + lcptr->from_value.length;
memcpy(merged_value,
lcqptr->merged_value.str, lcqptr->merged_value.length);
merged_value += lcqptr->merged_value.length;
......@@ -1273,9 +1293,6 @@ int spider_conn_queue_and_merge_loop_check(
goto error_hash_insert_queue;
}
lcptr->flag = SPIDER_LOP_CHK_MERAGED;
lcptr->next = NULL;
if (!conn->loop_check_meraged_first)
conn->loop_check_meraged_first = lcptr;
}
DBUG_RETURN(0);
......@@ -1296,7 +1313,6 @@ int spider_conn_queue_and_merge_loop_check(
int spider_conn_reset_queue_loop_check(
SPIDER_CONN *conn
) {
int error_num;
SPIDER_CONN_LOOP_CHECK *lcptr;
DBUG_ENTER("spider_conn_reset_queue_loop_check");
uint l = 0;
......@@ -1318,30 +1334,8 @@ int spider_conn_reset_queue_loop_check(
++l;
}
lcptr = conn->loop_check_ignored_first;
while (lcptr)
{
lcptr->flag = 0;
if ((error_num = spider_conn_queue_and_merge_loop_check(conn, lcptr)))
{
goto error_queue_and_merge;
}
lcptr = lcptr->next;
}
conn->loop_check_meraged_first = NULL;
pthread_mutex_unlock(&conn->loop_check_mutex);
DBUG_RETURN(0);
error_queue_and_merge:
lcptr = lcptr->next;
while (lcptr)
{
lcptr->flag = 0;
lcptr = lcptr->next;
}
conn->loop_check_meraged_first = NULL;
pthread_mutex_unlock(&conn->loop_check_mutex);
DBUG_RETURN(error_num);
}
int spider_conn_queue_loop_check(
......@@ -1352,7 +1346,7 @@ int spider_conn_queue_loop_check(
int error_num = HA_ERR_OUT_OF_MEM;
uint conn_link_idx = spider->conn_link_idx[link_idx], buf_sz;
char path[FN_REFLEN + 1];
char *tmp_name, *from_name, *cur_name, *to_name, *full_name, *from_value,
char *tmp_name, *cur_name, *to_name, *full_name, *from_value,
*merged_value;
user_var_entry *loop_check;
char *loop_check_buf;
......@@ -1363,6 +1357,17 @@ int spider_conn_queue_loop_check(
LEX_CSTRING lex_str, from_str, to_str;
DBUG_ENTER("spider_conn_queue_loop_check");
DBUG_PRINT("info", ("spider conn=%p", conn));
/*
construct loop check user var name (left hand side) into
lex_str. It is of the format
spider_lc_<spider_table_name>
So if the spider table name is ./test/t1, then the constructed
user var name is:
spider_lc_./test/t1
*/
lex_str.length = top_share->path.length + SPIDER_SQL_LOP_CHK_PRM_PRF_LEN;
buf_sz = lex_str.length + 2;
loop_check_buf = (char *) my_alloca(buf_sz);
......@@ -1388,6 +1393,16 @@ int spider_conn_queue_loop_check(
} else {
lex_str.str = loop_check->value;
lex_str.length = loop_check->length;
/*
Validate that there are at least four dashes in the user var
value: -<mac_addr>-<proc_id>-<table_name>-
Note: if the value is merged from multiple values, such as
"-<mac1>-<pid1>-<table_name1>--<mac2>-<pid2>-<table_name2>--<mac3>-<pid3>-<table_name3>-"
then only the first component is put into from_str
*/
DBUG_PRINT("info", ("spider from_str=%s", lex_str.str));
if (unlikely(!(tmp_name = strchr(loop_check->value, '-'))))
{
......@@ -1415,12 +1430,23 @@ int spider_conn_queue_loop_check(
}
else
{
/*
Validation passed. Put the first component of rhs in from_str
*/
from_str.str = lex_str.str;
from_str.length = tmp_name - lex_str.str + 1;
}
}
my_afree(loop_check_buf);
/*
construct loop_check_buf as <from_str>-<cur>-<to_str> e.g.
"-<mac>-<pid>-./test/t0--./test/t1-./test/t2", later used as
full_name
from_str is the first component in the user var value (RHS) or
empty if user var value is empty, cur is the spider table, to_str
is the remote data node table
*/
to_str.length = build_table_filename(path, FN_REFLEN,
share->tgt_dbs[conn_link_idx] ? share->tgt_dbs[conn_link_idx] : "",
share->tgt_table_names[conn_link_idx], "", 0);
......@@ -1457,7 +1483,7 @@ int spider_conn_queue_loop_check(
lcptr = (SPIDER_CONN_LOOP_CHECK *) my_hash_search(
&conn->loop_checked, (uchar *) loop_check_buf, buf_sz - 1);
#endif
if (unlikely(
if (
!lcptr ||
(
!lcptr->flag &&
......@@ -1466,7 +1492,7 @@ int spider_conn_queue_loop_check(
memcmp(lcptr->from_value.str, lex_str.str, lex_str.length)
)
)
))
)
{
if (unlikely(lcptr))
{
......@@ -1482,7 +1508,6 @@ int spider_conn_queue_loop_check(
DBUG_PRINT("info", ("spider alloc_lcptr"));
if (unlikely(!spider_bulk_malloc(spider_current_trx, 272, MYF(MY_WME),
&lcptr, (uint) (sizeof(SPIDER_CONN_LOOP_CHECK)),
&from_name, (uint) (from_str.length + 1),
&cur_name, (uint) (top_share->path.length + 1),
&to_name, (uint) (to_str.length + 1),
&full_name, (uint) (buf_sz),
......@@ -1495,9 +1520,6 @@ int spider_conn_queue_loop_check(
goto error_alloc_loop_check;
}
lcptr->flag = 0;
lcptr->from_name.str = from_name;
lcptr->from_name.length = from_str.length;
memcpy(from_name, from_str.str, from_str.length + 1);
lcptr->cur_name.str = cur_name;
lcptr->cur_name.length = top_share->path.length;
memcpy(cur_name, top_share->path.str, top_share->path.length + 1);
......@@ -1510,12 +1532,19 @@ int spider_conn_queue_loop_check(
lcptr->from_value.str = from_value;
lcptr->from_value.length = lex_str.length;
memcpy(from_value, lex_str.str, lex_str.length + 1);
/*
merged_value will only be populated later, in
spider_conn_queue_and_merge_loop_check()
*/
lcptr->merged_value.str = merged_value;
#ifdef SPIDER_HAS_HASH_VALUE_TYPE
lcptr->hash_value_to = my_calc_hash(&conn->loop_checked,
lcptr->hash_value_to = my_calc_hash(&conn->loop_check_queue,
(uchar *) to_str.str, to_str.length);
lcptr->hash_value_full = hash_value;
#endif
/*
Mark as checked. It will be added to loop_check_queue in
spider_conn_queue_and_merge_loop_check() below for checking
*/
#ifdef HASH_UPDATE_WITH_HASH_VALUE
if (unlikely(my_hash_insert_with_hash_value(&conn->loop_checked,
lcptr->hash_value_full, (uchar *) lcptr)))
......@@ -1527,19 +1556,11 @@ int spider_conn_queue_loop_check(
goto error_hash_insert;
}
} else {
/* Already marked as checked, ignore and return. */
if (!lcptr->flag)
{
DBUG_PRINT("info", ("spider add to ignored list"));
lcptr->flag |= SPIDER_LOP_CHK_IGNORED;
lcptr->next = NULL;
if (!conn->loop_check_ignored_first)
{
conn->loop_check_ignored_first = lcptr;
conn->loop_check_ignored_last = lcptr;
} else {
conn->loop_check_ignored_last->next = lcptr;
conn->loop_check_ignored_last = lcptr;
}
}
pthread_mutex_unlock(&conn->loop_check_mutex);
my_afree(loop_check_buf);
......
......@@ -26,24 +26,62 @@
#define SPIDER_SIMPLE_CHECKSUM_TABLE 4
#endif
/*
The SPIDER_CONN_LOOP_CHECK has been added to the loop_check queue to
check for self-reference.
*/
#define SPIDER_LOP_CHK_QUEUED (1 << 0)
/*
The SPIDER_CONN_LOOP_CHECK is a merge of multiple
SPIDER_CONN_LOOP_CHECKs with the same data node table
*/
#define SPIDER_LOP_CHK_MERAGED (1 << 1)
/*
The SPIDER_CONN_LOOP_CHECK has been ignored because it has already
been marked as checked
*/
#define SPIDER_LOP_CHK_IGNORED (1 << 2)
/* Used for self-reference check. */
typedef struct st_spider_conn_loop_check
{
/*
Could be 0, SPIDER_LOP_CHK_QUEUED, SPIDER_LOP_CHK_MERAGED, or
SPIDER_LOP_CHK_IGNORED
*/
uint flag;
#ifdef SPIDER_HAS_HASH_VALUE_TYPE
/* hash value of to_name, used for the hash conn->loop_checked */
my_hash_value_type hash_value_to;
my_hash_value_type hash_value_full;
#endif
LEX_CSTRING from_name;
/*
The fully qualified name of the current spider table, which will
also be used to construct the user var name to set in the data
node
*/
LEX_CSTRING cur_name;
/*
The fully qualified data node table name, also used as key in
conn->loop_check_queue
*/
LEX_CSTRING to_name;
/*
A concatenation of from_value, cur_name and to_name, used as key
in hash conn->loop_checked
*/
LEX_CSTRING full_name;
/*
The first component of the uservar value on the current server,
consisting of information of a table that uses the current spider
table as a data node
*/
LEX_CSTRING from_value;
/*
The uservar value to set in the data node, a concatenation of info
of tables, mac addresses and process ids of tables that use the
current spider table as the data node
*/
LEX_CSTRING merged_value;
st_spider_conn_loop_check *next;
} SPIDER_CONN_LOOP_CHECK;
uchar *spider_conn_get_key(
......
......@@ -66,7 +66,7 @@ spider_db_conn::spider_db_conn(
DBUG_VOID_RETURN;
}
int spider_db_conn::fin_loop_check()
int spider_db_conn::fin_loop_check() /* reset flags of all relevant SPIDER_CONN_LOOP_CHECKs */
{
st_spider_conn_loop_check *lcptr;
DBUG_ENTER("spider_db_conn::fin_loop_check");
......@@ -82,20 +82,6 @@ int spider_db_conn::fin_loop_check()
}
my_hash_reset(&conn->loop_check_queue);
}
lcptr = conn->loop_check_ignored_first;
while (lcptr)
{
lcptr->flag = 0;
lcptr = lcptr->next;
}
conn->loop_check_ignored_first = NULL;
lcptr = conn->loop_check_meraged_first;
while (lcptr)
{
lcptr->flag = 0;
lcptr = lcptr->next;
}
conn->loop_check_meraged_first = NULL;
DBUG_RETURN(0);
}
......
......@@ -5283,6 +5283,22 @@ int spider_db_mbase_util::append_time_zone(
DBUG_RETURN(0);
}
/*
iterate over loop_check_queue and build queries for loop check
The query is in the form of
set @`spider_lc_.<to_table>` =
'-<mac1>-<pid1>-<from_table1>--<mac2>-<pid2>-<from_table2>-...'
where from_table1, from_table2 etc. are spider tables whose direct
or indirect remote data nodes contain to_table.
e.g. if t0->t1->t2, then the query could be:
set @`spider_lc_./test/t2` =
'-234567890abc-def012-./test/t1--1234567890ab-cdef01-./test/t0-'
*/
int spider_db_mbase_util::append_loop_check(
spider_string *str,
SPIDER_CONN *conn
......
......@@ -793,19 +793,24 @@ typedef struct st_spider_conn
SPIDER_IP_PORT_CONN *ip_port_conn;
pthread_mutex_t loop_check_mutex;
/*
A hash of SPIDER_CONN_LOOP_CHECK, indexed by
SPIDER_CONN_LOOP_CHECK::full_name
*/
HASH loop_checked;
uint loop_checked_id;
const char *loop_checked_func_name;
const char *loop_checked_file_name;
ulong loop_checked_line_no;
/*
A hash of SPIDER_CONN_LOOP_CHECK, indexed by
SPIDER_CONN_LOOP_CHECK::to_name
*/
HASH loop_check_queue;
uint loop_check_queue_id;
const char *loop_check_queue_func_name;
const char *loop_check_queue_file_name;
ulong loop_check_queue_line_no;
SPIDER_CONN_LOOP_CHECK *loop_check_ignored_first;
SPIDER_CONN_LOOP_CHECK *loop_check_ignored_last;
SPIDER_CONN_LOOP_CHECK *loop_check_meraged_first;
} SPIDER_CONN;
typedef struct st_spider_lgtm_tblhnd_share
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment