summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/m_ctype.h4
-rw-r--r--mysql-test/r/ctype_utf8.result12
-rw-r--r--mysql-test/r/ctype_utf8mb4.result12
-rw-r--r--mysql-test/r/mysql.result12
-rw-r--r--mysql-test/t/ctype_utf8.test16
-rw-r--r--mysql-test/t/ctype_utf8mb4.test15
-rw-r--r--mysql-test/t/mysql.test24
-rw-r--r--sql/sql_acl.cc12
-rw-r--r--sql/sql_class.cc86
-rw-r--r--sql/sql_class.h40
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_string.h11
-rw-r--r--sql/sql_yacc.yy4
13 files changed, 232 insertions, 21 deletions
diff --git a/include/m_ctype.h b/include/m_ctype.h
index 7df59488029..8300619d5c7 100644
--- a/include/m_ctype.h
+++ b/include/m_ctype.h
@@ -889,6 +889,10 @@ uint32 my_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
cannot be represented in the destination character set was found,
or to NULL if all characters in the given range were successfully
converted.
+
+ "src" is allowed to be a NULL pointer. In this case "src_length" must
+ be equal to 0. All "status" members are initialized to NULL, and 0 is
+ returned.
*/
size_t my_convert_fix(CHARSET_INFO *dstcs, char *dst, size_t dst_length,
CHARSET_INFO *srccs, const char *src, size_t src_length,
diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index 2779ea5fa0f..90f679bc0db 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -9332,3 +9332,15 @@ DROP TABLE allbytes;
#
# End of 10.0 tests
#
+#
+# Start of 10.1 tests
+#
+#
+# MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+#
+SET NAMES utf8;
+SELECT * FROM `test😁😁test`;
+ERROR HY000: Invalid utf8 character string: 'test\xF0\x9F\x98\x81\xF0\x9F\x98\x81test'
+#
+# End of 10.1 tests
+#
diff --git a/mysql-test/r/ctype_utf8mb4.result b/mysql-test/r/ctype_utf8mb4.result
index d8f4eb32132..ee91c93cd5b 100644
--- a/mysql-test/r/ctype_utf8mb4.result
+++ b/mysql-test/r/ctype_utf8mb4.result
@@ -3356,3 +3356,15 @@ DROP TABLE t1;
#
# End of tests
#
+#
+# Start of 10.1 tests
+#
+#
+# MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+#
+SET NAMES utf8mb4;
+SELECT * FROM `test😁😁test`;
+ERROR HY000: Invalid utf8mb4 character string: 'test\xF0\x9F\x98\x81\xF0\x9F\x98\x81test'
+#
+# End of 10.1 tests
+#
diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result
index cb705d285fe..82d69557f0d 100644
--- a/mysql-test/r/mysql.result
+++ b/mysql-test/r/mysql.result
@@ -519,5 +519,17 @@ a
| a |
| aaaaaaaaaaaaaaaaa |
+-------------------+
+#
+# Start of 10.1 tests
+#
+#
+# MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+#
+#
+# End of 10.1 tests
+#
+ERROR 1300 (HY000): Invalid utf8 character string: 'test\xF0\x9F\x98\x81 '
+ERROR 1300 (HY000): Invalid binary character string: 'test\xF0\x9F\x98\x81 '
+ERROR 1300 (HY000) at line 2: Invalid utf8 character string: 'test\xF0\x9F\x98\x81'
End of tests
diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test
index af5f4b8ccf8..7897462aa02 100644
--- a/mysql-test/t/ctype_utf8.test
+++ b/mysql-test/t/ctype_utf8.test
@@ -1756,3 +1756,19 @@ let $ctype_unescape_combinations=selected;
--echo #
--echo # End of 10.0 tests
--echo #
+
+
+--echo #
+--echo # Start of 10.1 tests
+--echo #
+
+--echo #
+--echo # MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+--echo #
+SET NAMES utf8;
+--error ER_INVALID_CHARACTER_STRING
+SELECT * FROM `test😁😁test`;
+
+--echo #
+--echo # End of 10.1 tests
+--echo #
diff --git a/mysql-test/t/ctype_utf8mb4.test b/mysql-test/t/ctype_utf8mb4.test
index 232dd8fcb5d..55909d6a6c8 100644
--- a/mysql-test/t/ctype_utf8mb4.test
+++ b/mysql-test/t/ctype_utf8mb4.test
@@ -1880,3 +1880,18 @@ DROP TABLE t1;
--echo #
--echo # End of tests
--echo #
+
+--echo #
+--echo # Start of 10.1 tests
+--echo #
+
+--echo #
+--echo # MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+--echo #
+SET NAMES utf8mb4;
+--error ER_INVALID_CHARACTER_STRING
+SELECT * FROM `test😁😁test`;
+
+--echo #
+--echo # End of 10.1 tests
+--echo #
diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test
index 2b4b1e69ab6..c328ab19ffd 100644
--- a/mysql-test/t/mysql.test
+++ b/mysql-test/t/mysql.test
@@ -608,5 +608,29 @@ EOF
#
--exec $MYSQL -t -N -e "SELECT 'a' union select 'aaaaaaaaaaaaaaaaa'"
+--echo #
+--echo # Start of 10.1 tests
+--echo #
+
+--echo #
+--echo # MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database
+--echo #
+
+--echo #
+--echo # End of 10.1 tests
+--echo #
+
+--error 1
+--exec $MYSQL --default-character-set=utf8 -e "select 1" "test😁 " 2>&1
+--error 1
+--exec $MYSQL --default-character-set=binary -e "select 1" "test😁 " 2>&1
+--write_file $MYSQLTEST_VARDIR/tmp/mdev-6572.sql
+SET NAMES utf8;
+USE test😁 ;
+EOF
+--error 1
+--exec $MYSQL --default-character-set=utf8 < $MYSQLTEST_VARDIR/tmp/mdev-6572.sql 2>&1
+--remove_file $MYSQLTEST_VARDIR/tmp/mdev-6572.sql
+
--echo
--echo End of tests
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index aaa12763cec..471dd0eac91 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -11701,7 +11701,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
char *passwd= strend(user)+1;
uint user_len= passwd - user - 1, db_len;
char *db= passwd;
- char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
uint dummy_errors;
@@ -11738,12 +11737,9 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
/* Since 4.1 all database names are stored in utf8 */
- if (db)
- {
- db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
- db, db_len, thd->charset(), &dummy_errors);
- db= db_buff;
- }
+ if (thd->copy_with_error(system_charset_info, &mpvio->db,
+ thd->charset(), db, db_len))
+ return packet_error;
user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
system_charset_info, user, user_len,
@@ -11773,8 +11769,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Security_context *sctx= thd->security_ctx;
- if (thd->make_lex_string(&mpvio->db, db, db_len) == 0)
- return packet_error; /* The error is set by make_lex_string(). */
my_free(sctx->user);
if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
return packet_error; /* The error is set by my_strdup(). */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index b640a261529..39737a3f0d3 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2231,18 +2231,88 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length,
CHARSET_INFO *from_cs)
{
- DBUG_ENTER("convert_string");
+ DBUG_ENTER("THD::convert_string");
size_t new_length= to_cs->mbmaxlen * from_length;
uint dummy_errors;
- if (!(to->str= (char*) alloc(new_length+1)))
- {
- to->length= 0; // Safety fix
- DBUG_RETURN(1); // EOM
- }
+ if (alloc_lex_string(to, new_length + 1))
+ DBUG_RETURN(true); // EOM
to->length= copy_and_convert((char*) to->str, new_length, to_cs,
from, from_length, from_cs, &dummy_errors);
- to->str[to->length]=0; // Safety
- DBUG_RETURN(0);
+ to->str[to->length]= 0; // Safety
+ DBUG_RETURN(false);
+}
+
+
+/*
+ Convert a string between two character sets.
+ dstcs and srccs cannot be &my_charset_bin.
+*/
+bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs, const char *src, uint src_length,
+ String_copier *status)
+{
+ DBUG_ENTER("THD::convert_fix");
+ size_t dst_length= dstcs->mbmaxlen * src_length;
+ if (alloc_lex_string(dst, dst_length + 1))
+ DBUG_RETURN(true); // EOM
+ dst->length= status->convert_fix(dstcs, (char*) dst->str, dst_length,
+ srccs, src, src_length, src_length);
+ dst->str[dst->length]= 0; // Safety
+ DBUG_RETURN(false);
+}
+
+
+/*
+ Copy or convert a string.
+*/
+bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs, const char *src, uint src_length,
+ String_copier *status)
+{
+ DBUG_ENTER("THD::copy_fix");
+ size_t dst_length= dstcs->mbmaxlen * src_length;
+ if (alloc_lex_string(dst, dst_length + 1))
+ DBUG_RETURN(true); // EOM
+ dst->length= status->well_formed_copy(dstcs, dst->str, dst_length,
+ srccs, src, src_length, src_length);
+ dst->str[dst->length]= '\0';
+ DBUG_RETURN(false);
+}
+
+
+class String_copier_with_error: public String_copier
+{
+public:
+ bool check_errors(CHARSET_INFO *srccs, const char *src, uint src_length)
+ {
+ if (most_important_error_pos())
+ {
+ ErrConvString err(src, src_length, &my_charset_bin);
+ my_error(ER_INVALID_CHARACTER_STRING, MYF(0), srccs->csname, err.ptr());
+ return true;
+ }
+ return false;
+ }
+};
+
+
+bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs,
+ const char *src, uint src_length)
+{
+ String_copier_with_error status;
+ return convert_fix(dstcs, dst, srccs, src, src_length, &status) ||
+ status.check_errors(srccs, src, src_length);
+}
+
+
+bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs,
+ const char *src, uint src_length)
+{
+ String_copier_with_error status;
+ return copy_fix(dstcs, dst, srccs, src, src_length, &status) ||
+ status.check_errors(srccs, src, src_length);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 0049b971f48..47426693d3b 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3106,9 +3106,49 @@ public:
return make_lex_string(lex_str, str, length);
}
+ // Allocate LEX_STRING for character set conversion
+ bool alloc_lex_string(LEX_STRING *dst, uint length)
+ {
+ if ((dst->str= (char*) alloc(length)))
+ return false;
+ dst->length= 0; // Safety
+ return true; // EOM
+ }
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length,
CHARSET_INFO *from_cs);
+ /*
+ Convert a strings between character sets.
+ Uses my_convert_fix(), which uses an mb_wc .. mc_mb loop internally.
+ dstcs and srccs cannot be &my_charset_bin.
+ */
+ bool convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs, const char *src, uint src_length,
+ String_copier *status);
+
+ /*
+ Same as above, but additionally sends ER_INVALID_CHARACTER_STRING
+ in case of bad byte sequences or Unicode conversion problems.
+ */
+ bool convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs,
+ const char *src, uint src_length);
+
+ /*
+ If either "dstcs" or "srccs" is &my_charset_bin,
+ then performs native copying using cs->cset->copy_fix().
+ Otherwise, performs Unicode conversion using convert_fix().
+ */
+ bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs, const char *src, uint src_length,
+ String_copier *status);
+
+ /*
+ Same as above, but additionally sends ER_INVALID_CHARACTER_STRING
+ in case of bad byte sequences or Unicode conversion problems.
+ */
+ bool copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
+ CHARSET_INFO *srccs, const char *src, uint src_length);
bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 640eee093cc..3109b2ffe86 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1320,8 +1320,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
LEX_STRING tmp;
status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
- thd->convert_string(&tmp, system_charset_info,
- packet, packet_length, thd->charset());
+ if (thd->copy_with_error(system_charset_info, &tmp,
+ thd->charset(), packet, packet_length))
+ break;
if (!mysql_change_db(thd, &tmp, FALSE))
{
general_log_write(thd, command, thd->db, thd->db_length);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 4c02a46cf67..4a23d65d6a8 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -58,6 +58,17 @@ public:
cannot_convert_error_pos();
}
/*
+ Convert a string between character sets.
+ "dstcs" and "srccs" cannot be &my_charset_bin.
+ */
+ uint convert_fix(CHARSET_INFO *dstcs, char *dst, uint dst_length,
+ CHARSET_INFO *srccs, const char *src, uint src_length,
+ uint nchars)
+ {
+ return my_convert_fix(dstcs, dst, dst_length,
+ srccs, src, src_length, nchars, this);
+ }
+ /*
Copy a string. Fix bad bytes/characters one Unicode conversion,
break on bad bytes in case of non-Unicode copying.
*/
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index fe44fd21675..cf933e0be3b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13966,8 +13966,8 @@ IDENT_sys:
}
else
{
- if (thd->convert_string(&$$, system_charset_info,
- $1.str, $1.length, thd->charset()))
+ if (thd->convert_with_error(system_charset_info, &$$,
+ thd->charset(), $1.str, $1.length))
MYSQL_YYABORT;
}
}