summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2021-10-13 12:57:57 +0400
committerAlexander Barkov <bar@mariadb.com>2021-10-14 10:16:23 +0400
commita2a42f4eba7409264d4b4fb2dc7c04e40c50bd25 (patch)
tree86e8de3020f8534b0efac9b369bcfa4dcf0b83cb
parentbbae2d398f866b00f7e8ad71984884ff3375df52 (diff)
downloadmariadb-git-a2a42f4eba7409264d4b4fb2dc7c04e40c50bd25.tar.gz
MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
There were two independent problems which lead to the crash and to the non-relevant records returned in I_S queries: - The code in the I_S implementation was not secure about values with 0x00 bytes. It's fixed by using check_db_name() and check_table_name() inside make_table_name_list(), and by adding the test for 0x00 inside check_table_name(). - The code in Item_string::print() did not convert strings without introducers when restoring the CREATE VIEW statement from an Item tree. This made wrong literals inside the "query" line in the view FRM file in cases when the VIEW parse time character_set_client!=character_set_connection. That's fixed by adding a proper conversion. This change also fixed a similar problem in SHOW PROCEDURE CODE - the literals were displayed in wrong character set in SP instructions in cases when the SP parse time character_set_client!=character_set_connection.
-rw-r--r--mysql-test/r/ctype_utf16le.result33
-rw-r--r--mysql-test/r/ctype_utf8.result17
-rw-r--r--mysql-test/r/information_schema.result20
-rw-r--r--mysql-test/r/sp-code.result24
-rw-r--r--mysql-test/t/ctype_utf16le.test37
-rw-r--r--mysql-test/t/ctype_utf8.test18
-rw-r--r--mysql-test/t/information_schema.test21
-rw-r--r--mysql-test/t/sp-code.test24
-rw-r--r--sql/item.cc44
-rw-r--r--sql/sql_show.cc7
-rw-r--r--sql/table.cc15
11 files changed, 257 insertions, 3 deletions
diff --git a/mysql-test/r/ctype_utf16le.result b/mysql-test/r/ctype_utf16le.result
index a43ed6ee538..bc28f89b7ea 100644
--- a/mysql-test/r/ctype_utf16le.result
+++ b/mysql-test/r/ctype_utf16le.result
@@ -3000,5 +3000,38 @@ DROP TABLE t1;
#
SET STORAGE_ENGINE=Default;
#
+# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+#
+SET NAMES utf8;
+SET SESSION character_set_connection= utf16le;
+CREATE TABLE kv (v BLOB);
+CREATE TABLE t (a INT);
+CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+LOAD DATA INFILE 'MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv;
+SELECT * FROM kv WHERE v LIKE _binary'query=%';
+v
+query=select `information_schema`.`TABLES`.`TABLE_CATALOG` AS `TABLE_CATALOG`,`information_schema`.`TABLES`.`TABLE_SCHEMA` AS `TABLE_SCHEMA`,`information_schema`.`TABLES`.`TABLE_NAME` AS `TABLE_NAME`,`information_schema`.`TABLES`.`TABLE_TYPE` AS `TABLE_TYPE`,`information_schema`.`TABLES`.`ENGINE` AS `ENGINE`,`information_schema`.`TABLES`.`VERSION` AS `VERSION`,`information_schema`.`TABLES`.`ROW_FORMAT` AS `ROW_FORMAT`,`information_schema`.`TABLES`.`TABLE_ROWS` AS `TABLE_ROWS`,`information_schema`.`TABLES`.`AVG_ROW_LENGTH` AS `AVG_ROW_LENGTH`,`information_schema`.`TABLES`.`DATA_LENGTH` AS `DATA_LENGTH`,`information_schema`.`TABLES`.`MAX_DATA_LENGTH` AS `MAX_DATA_LENGTH`,`information_schema`.`TABLES`.`INDEX_LENGTH` AS `INDEX_LENGTH`,`information_schema`.`TABLES`.`DATA_FREE` AS `DATA_FREE`,`information_schema`.`TABLES`.`AUTO_INCREMENT` AS `AUTO_INCREMENT`,`information_schema`.`TABLES`.`CREATE_TIME` AS `CREATE_TIME`,`information_schema`.`TABLES`.`UPDATE_TIME` AS `UPDATE_TIME`,`information_schema`.`TABLES`.`CHECK_TIME` AS `CHECK_TIME`,`information_schema`.`TABLES`.`TABLE_COLLATION` AS `TABLE_COLLATION`,`information_schema`.`TABLES`.`CHECKSUM` AS `CHECKSUM`,`information_schema`.`TABLES`.`CREATE_OPTIONS` AS `CREATE_OPTIONS`,`information_schema`.`TABLES`.`TABLE_COMMENT` AS `TABLE_COMMENT` from `INFORMATION_SCHEMA`.`TABLES` where `information_schema`.`TABLES`.`TABLE_NAME` = 't1'
+TRUNCATE TABLE kv;
+SELECT * FROM v;
+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
+LOCK TABLE t WRITE;
+UNLOCK TABLES;
+DROP VIEW v;
+DROP TABLE t;
+DROP TABLE kv;
+CREATE TABLE t (a INT);
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+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
+LOCK TABLE t WRITE;
+UNLOCK TABLES;
+DROP TABLE t;
+CREATE TABLE t (a INT);
+SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+TABLE_NAME HEX(TABLE_NAME)
+SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
+TABLE_NAME TABLE_SCHEMA HEX(TABLE_NAME)
+DROP TABLE t;
+SET NAMES utf8;
+#
# End of 10.2 tests
#
diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index 7189629b570..18398f2556a 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -11239,5 +11239,22 @@ DROP TABLE t1;
#
SET STORAGE_ENGINE=Default;
#
+# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+#
+SET NAMES utf8;
+SET SESSION character_set_connection=latin1;
+CREATE VIEW v1 AS SELECT 'ä' AS c1;
+SELECT c1, HEX(c1) FROM v1;
+c1 HEX(c1)
+ä E4
+CREATE TABLE kv (v BLOB);
+LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
+SELECT * FROM kv WHERE v LIKE _binary'query=%';
+v
+query=select 'ä' AS `c1`
+DROP TABLE kv;
+DROP VIEW v1;
+SET NAMES utf8;
+#
# End of 10.2 tests
#
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index c7153bd6383..cc9cf842570 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -2210,5 +2210,25 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9;
ERROR HY000: Sort aborted:
DROP VIEW v;
#
+# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+#
+CREATE TABLE t (a INT);
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+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
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
+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
+DROP TABLE t;
+CREATE TABLE `a/~.b` (a INT);
+SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b';
+TABLE_SCHEMA TABLE_NAME
+test a/~.b
+DROP TABLE `a/~.b`;
+CREATE DATABASE `a/~.b`;
+CREATE TABLE `a/~.b`.t1 (a INT);
+SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b';
+TABLE_SCHEMA TABLE_NAME
+a/~.b t1
+DROP DATABASE `a/~.b`;
+#
# End of 10.2 Test
#
diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result
index 67932447c2a..4ad79f50d45 100644
--- a/mysql-test/r/sp-code.result
+++ b/mysql-test/r/sp-code.result
@@ -971,3 +971,27 @@ Pos Instruction
DROP PROCEDURE testp_bug11763507;
DROP FUNCTION testf_bug11763507;
#END OF BUG#11763507 test.
+#
+# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+#
+SET NAMES utf8;
+SET SESSION character_set_connection=latin1;
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE a VARCHAR(10) CHARACTER SET utf8;
+SET a='ä';
+SELECT a, 'ä' AS b;
+END;
+$$
+SHOW PROCEDURE CODE p1;
+Pos Instruction
+0 set a@0 NULL
+1 set a@0 'ä'
+2 stmt 0 "SELECT a, 'ä' AS b"
+CALL p1;
+a b
+ä ä
+DROP PROCEDURE p1;
+#
+# End of 10.2 tests
+#
diff --git a/mysql-test/t/ctype_utf16le.test b/mysql-test/t/ctype_utf16le.test
index 204df136274..671100c2d9d 100644
--- a/mysql-test/t/ctype_utf16le.test
+++ b/mysql-test/t/ctype_utf16le.test
@@ -3,6 +3,7 @@
-- source include/have_utf32.inc
-- source include/have_utf8mb4.inc
+let $MYSQLD_DATADIR= `select @@datadir`;
SET TIME_ZONE='+03:00';
@@ -811,5 +812,41 @@ let $coll_pad='utf16le_bin';
--source include/ctype_pad_all_engines.inc
--echo #
+--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+--echo #
+
+
+
+SET NAMES utf8;
+SET SESSION character_set_connection= utf16le;
+
+CREATE TABLE kv (v BLOB);
+CREATE TABLE t (a INT);
+CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
+--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
+eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv;
+SELECT * FROM kv WHERE v LIKE _binary'query=%';
+TRUNCATE TABLE kv;
+SELECT * FROM v;
+LOCK TABLE t WRITE;
+UNLOCK TABLES;
+DROP VIEW v;
+DROP TABLE t;
+DROP TABLE kv;
+
+CREATE TABLE t (a INT);
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+LOCK TABLE t WRITE;
+UNLOCK TABLES;
+DROP TABLE t;
+
+CREATE TABLE t (a INT);
+SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
+DROP TABLE t;
+
+SET NAMES utf8;
+
+--echo #
--echo # End of 10.2 tests
--echo #
diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test
index 9a64821db66..b34de4175e9 100644
--- a/mysql-test/t/ctype_utf8.test
+++ b/mysql-test/t/ctype_utf8.test
@@ -2,6 +2,8 @@
# Tests with the utf8 character set
#
+let $MYSQLD_DATADIR= `select @@datadir`;
+
let collation=utf8_unicode_ci;
--source include/have_collation.inc
SET TIME_ZONE='+03:00';
@@ -2166,5 +2168,21 @@ let $coll_pad='utf8_bin';
--source include/ctype_pad_all_engines.inc
--echo #
+--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+--echo #
+
+SET NAMES utf8;
+SET SESSION character_set_connection=latin1;
+CREATE VIEW v1 AS SELECT 'ä' AS c1;
+SELECT c1, HEX(c1) FROM v1;
+CREATE TABLE kv (v BLOB);
+--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
+eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
+SELECT * FROM kv WHERE v LIKE _binary'query=%';
+DROP TABLE kv;
+DROP VIEW v1;
+SET NAMES utf8;
+
+--echo #
--echo # End of 10.2 tests
--echo #
diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test
index 9ff94d2deb7..b7f4a7e4407 100644
--- a/mysql-test/t/information_schema.test
+++ b/mysql-test/t/information_schema.test
@@ -1935,5 +1935,26 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9;
DROP VIEW v;
--echo #
+--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+--echo #
+
+# Expect empty sets if requested TABLE_NAME or TABLE_SCHEMA with zero bytes
+CREATE TABLE t (a INT);
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1');
+SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1');
+DROP TABLE t;
+
+# Make sure check_table_name() does not reject special characters
+CREATE TABLE `a/~.b` (a INT);
+SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b';
+DROP TABLE `a/~.b`;
+
+# Make sure check_db_name() does not reject special characters
+CREATE DATABASE `a/~.b`;
+CREATE TABLE `a/~.b`.t1 (a INT);
+SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b';
+DROP DATABASE `a/~.b`;
+
+--echo #
--echo # End of 10.2 Test
--echo #
diff --git a/mysql-test/t/sp-code.test b/mysql-test/t/sp-code.test
index 129a68204ba..0f19627c78c 100644
--- a/mysql-test/t/sp-code.test
+++ b/mysql-test/t/sp-code.test
@@ -735,3 +735,27 @@ DROP PROCEDURE testp_bug11763507;
DROP FUNCTION testf_bug11763507;
--echo #END OF BUG#11763507 test.
+
+
+--echo #
+--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset
+--echo #
+
+SET NAMES utf8;
+SET SESSION character_set_connection=latin1;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+BEGIN
+ DECLARE a VARCHAR(10) CHARACTER SET utf8;
+ SET a='ä';
+ SELECT a, 'ä' AS b;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # End of 10.2 tests
+--echo #
diff --git a/sql/item.cc b/sql/item.cc
index 2a7c620b864..3ff0219c3b3 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3338,8 +3338,48 @@ void Item_string::print(String *str, enum_query_type query_type)
}
else
{
- // Caller wants a result in the charset of str_value.
- str_value.print(str);
+ /*
+ We're restoring a parse-able statement from an Item tree.
+ Make sure to revert character set conversions that previously
+ happened in the parser when Item_string was created.
+ */
+ if (print_introducer)
+ {
+ /*
+ Print the string as is, without conversion:
+ Strings with introducers are not converted in the parser.
+ */
+ str_value.print(str);
+ }
+ else
+ {
+ /*
+ Print the string with conversion.
+ Strings without introducers are converted in the parser,
+ from character_set_client to character_set_connection.
+
+ When restoring a CREATE VIEW statement,
+ - str_value.charsets() contains parse time character_set_connection
+ - str->charset() contains parse time character_set_client
+ So we convert the string back from parse-time character_set_connection
+ to parse time character_set_client.
+
+ In some cases, e.g. SHOW PROCEDURE CODE, it's also possible
+ that str->charset() is "utf8mb3" instead of parse time
+ character_set_client. In these cases we convert
+ here from the parse-time character_set_connection to utf8mb3.
+
+ QQ: perhaps the code behind SHOW PROCEDURE CODE should
+ also request the result in the parse-time character_set_client
+ (like the code restoring CREATE VIEW statements does),
+ rather than in utf8mb3:
+ - utf8mb3 does not work well with non-BMP characters (e.g. emoji).
+ - Simply changing utf8mb3 to utf8mb4 will not fully help:
+ some character sets have unassigned characters,
+ they get lost during during cs->utf8mb4->cs round trip.
+ */
+ str_value.print_with_conversion(str, str->charset());
+ }
}
str->append('\'');
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 721bb053343..710c68d2551 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -4227,7 +4227,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
if (!lookup_field_vals->wild_table_value &&
lookup_field_vals->table_value.str)
{
- if (lookup_field_vals->table_value.length > NAME_LEN)
+ if (check_table_name(lookup_field_vals->table_value.str,
+ lookup_field_vals->table_value.length,
+ false))
{
/*
Impossible value for a table name,
@@ -4264,6 +4266,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
return (schema_tables_add(thd, table_names,
lookup_field_vals->table_value.str));
+ if (check_db_name((LEX_STRING*)db_name))
+ return 0; // Impossible TABLE_SCHEMA name
+
find_files_result res= find_files(thd, table_names, db_name, path,
&lookup_field_vals->table_value);
if (res != FIND_FILES_OK)
diff --git a/sql/table.cc b/sql/table.cc
index 87b3c158a67..d4f8170e0af 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4183,6 +4183,21 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
if (check_for_path_chars &&
(*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR))
return 1;
+ /*
+ We don't allow zero byte in table/schema names:
+ - Some code still uses NULL-terminated strings.
+ Zero bytes will confuse this code.
+ - There is a little practical use of zero bytes in names anyway.
+ Note, if the string passed as "name" comes here
+ from the parser as an identifier, it does not contain zero bytes,
+ as the parser rejects zero bytes in identifiers.
+ But "name" can also come here from queries like this:
+ SELECT * FROM I_S.TABLES WHERE TABLE_NAME='str';
+ In this case "name" is a general string expression
+ and it can have any arbitrary bytes, including zero bytes.
+ */
+ if (*name == 0x00)
+ return 1;
name++;
name_length++;
}