Commit e6f67c64 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-6572 "USE dbname" with a bad sequence erroneously connects to a wrong database

parent 4cb86b79
...@@ -889,6 +889,10 @@ uint32 my_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, ...@@ -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, cannot be represented in the destination character set was found,
or to NULL if all characters in the given range were successfully or to NULL if all characters in the given range were successfully
converted. 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, size_t my_convert_fix(CHARSET_INFO *dstcs, char *dst, size_t dst_length,
CHARSET_INFO *srccs, const char *src, size_t src_length, CHARSET_INFO *srccs, const char *src, size_t src_length,
......
...@@ -9332,3 +9332,15 @@ DROP TABLE allbytes; ...@@ -9332,3 +9332,15 @@ DROP TABLE allbytes;
# #
# End of 10.0 tests # 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
#
...@@ -3356,3 +3356,15 @@ DROP TABLE t1; ...@@ -3356,3 +3356,15 @@ DROP TABLE t1;
# #
# End of tests # 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
#
...@@ -519,5 +519,17 @@ a ...@@ -519,5 +519,17 @@ a
| a | | a |
| aaaaaaaaaaaaaaaaa | | 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 End of tests
...@@ -1756,3 +1756,19 @@ let $ctype_unescape_combinations=selected; ...@@ -1756,3 +1756,19 @@ let $ctype_unescape_combinations=selected;
--echo # --echo #
--echo # End of 10.0 tests --echo # End of 10.0 tests
--echo # --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 #
...@@ -1880,3 +1880,18 @@ DROP TABLE t1; ...@@ -1880,3 +1880,18 @@ DROP TABLE t1;
--echo # --echo #
--echo # End of tests --echo # End of tests
--echo # --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 #
...@@ -608,5 +608,29 @@ EOF ...@@ -608,5 +608,29 @@ EOF
# #
--exec $MYSQL -t -N -e "SELECT 'a' union select 'aaaaaaaaaaaaaaaaa'" --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
--echo End of tests --echo End of tests
...@@ -11701,7 +11701,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, ...@@ -11701,7 +11701,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
char *passwd= strend(user)+1; char *passwd= strend(user)+1;
uint user_len= passwd - user - 1, db_len; uint user_len= passwd - user - 1, db_len;
char *db= passwd; 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 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
uint dummy_errors; uint dummy_errors;
...@@ -11738,12 +11737,9 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, ...@@ -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); char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
/* Since 4.1 all database names are stored in utf8 */ /* Since 4.1 all database names are stored in utf8 */
if (db) if (thd->copy_with_error(system_charset_info, &mpvio->db,
{ thd->charset(), db, db_len))
db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info, return packet_error;
db, db_len, thd->charset(), &dummy_errors);
db= db_buff;
}
user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1, user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
system_charset_info, user, user_len, system_charset_info, user, user_len,
...@@ -11773,8 +11769,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, ...@@ -11773,8 +11769,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Security_context *sctx= thd->security_ctx; 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); my_free(sctx->user);
if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME)))) if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
return packet_error; /* The error is set by my_strdup(). */ return packet_error; /* The error is set by my_strdup(). */
......
...@@ -2231,18 +2231,88 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, ...@@ -2231,18 +2231,88 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length, const char *from, uint from_length,
CHARSET_INFO *from_cs) CHARSET_INFO *from_cs)
{ {
DBUG_ENTER("convert_string"); DBUG_ENTER("THD::convert_string");
size_t new_length= to_cs->mbmaxlen * from_length; size_t new_length= to_cs->mbmaxlen * from_length;
uint dummy_errors; uint dummy_errors;
if (!(to->str= (char*) alloc(new_length+1))) if (alloc_lex_string(to, new_length + 1))
{ DBUG_RETURN(true); // EOM
to->length= 0; // Safety fix
DBUG_RETURN(1); // EOM
}
to->length= copy_and_convert((char*) to->str, new_length, to_cs, to->length= copy_and_convert((char*) to->str, new_length, to_cs,
from, from_length, from_cs, &dummy_errors); from, from_length, from_cs, &dummy_errors);
to->str[to->length]=0; // Safety to->str[to->length]= 0; // Safety
DBUG_RETURN(0); 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);
} }
......
...@@ -3106,9 +3106,49 @@ public: ...@@ -3106,9 +3106,49 @@ public:
return make_lex_string(lex_str, str, length); 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, bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length, const char *from, uint from_length,
CHARSET_INFO *from_cs); 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); bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs);
......
...@@ -1320,8 +1320,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1320,8 +1320,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{ {
LEX_STRING tmp; LEX_STRING tmp;
status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]); status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
thd->convert_string(&tmp, system_charset_info, if (thd->copy_with_error(system_charset_info, &tmp,
packet, packet_length, thd->charset()); thd->charset(), packet, packet_length))
break;
if (!mysql_change_db(thd, &tmp, FALSE)) if (!mysql_change_db(thd, &tmp, FALSE))
{ {
general_log_write(thd, command, thd->db, thd->db_length); general_log_write(thd, command, thd->db, thd->db_length);
......
...@@ -57,6 +57,17 @@ public: ...@@ -57,6 +57,17 @@ public:
return well_formed_error_pos() ? well_formed_error_pos() : return well_formed_error_pos() ? well_formed_error_pos() :
cannot_convert_error_pos(); 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, Copy a string. Fix bad bytes/characters one Unicode conversion,
break on bad bytes in case of non-Unicode copying. break on bad bytes in case of non-Unicode copying.
......
...@@ -13966,8 +13966,8 @@ IDENT_sys: ...@@ -13966,8 +13966,8 @@ IDENT_sys:
} }
else else
{ {
if (thd->convert_string(&$$, system_charset_info, if (thd->convert_with_error(system_charset_info, &$$,
$1.str, $1.length, thd->charset())) thd->charset(), $1.str, $1.length))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
} }
......
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