Commit f4beb929 authored by Alexander Barkov's avatar Alexander Barkov

Bug#11765108 (Bug#58036) client utf32, utf16, ucs2 should be disallowed, they crash server

Problem: ucs2 was correctly disallowed in "SET NAMES" only,
while mysql_real_connect() and mysql_change_user() still allowed
to use ucs2, which made server crash.

Fix: disallow ucs2 in mysql_real_connect() and mysql_change_user().

  @ sql/set_var.cc
    Using new function.

  @ sql/sql_acl.cc
    - Return error if character set initialization failed
    - Getting rid of pointer aliasing:
      Initialize user_name to NULL, to avoid double free().

  @ sql/sql_connect.cc
    - in case of unsupported client character set send error and return true
    - in case of success return false

  @ sql/sql_connect.h
    - changing return type for thd_init_client_charset() to bool,
      to return errors to the caller

  @ sql/sql_parse.h
    - introducing a new function, to reuse in all places where we need
    to check client character set.

  @ tests/mysql_client_test.c
    Adding test
parent 0db53b19
...@@ -776,7 +776,7 @@ int set_var_password::update(THD *thd) ...@@ -776,7 +776,7 @@ int set_var_password::update(THD *thd)
int set_var_collation_client::check(THD *thd) int set_var_collation_client::check(THD *thd)
{ {
/* Currently, UCS-2 cannot be used as a client character set */ /* Currently, UCS-2 cannot be used as a client character set */
if (character_set_client->mbminlen > 1) if (!is_supported_parser_charset(character_set_client))
{ {
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
character_set_client->csname); character_set_client->csname);
......
...@@ -7799,7 +7799,8 @@ class Thd_charset_adapter ...@@ -7799,7 +7799,8 @@ class Thd_charset_adapter
Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {} Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {}
bool init_client_charset(uint cs_number) bool init_client_charset(uint cs_number)
{ {
thd_init_client_charset(thd, cs_number); if (thd_init_client_charset(thd, cs_number))
return true;
thd->update_charset(); thd->update_charset();
return thd->is_error(); return thd->is_error();
} }
...@@ -8929,9 +8930,8 @@ server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors, ...@@ -8929,9 +8930,8 @@ server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors,
mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip; mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
mpvio->auth_info.host_or_ip_length= mpvio->auth_info.host_or_ip_length=
(unsigned int) strlen(thd->security_ctx->host_or_ip); (unsigned int) strlen(thd->security_ctx->host_or_ip);
mpvio->auth_info.user_name= thd->security_ctx->user; mpvio->auth_info.user_name= NULL;
mpvio->auth_info.user_name_length= thd->security_ctx->user ? mpvio->auth_info.user_name_length= 0;
(unsigned int) strlen(thd->security_ctx->user) : 0;
mpvio->connect_errors= connect_errors; mpvio->connect_errors= connect_errors;
mpvio->status= MPVIO_EXT::FAILURE; mpvio->status= MPVIO_EXT::FAILURE;
......
...@@ -370,8 +370,23 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) ...@@ -370,8 +370,23 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
} }
void thd_init_client_charset(THD *thd, uint cs_number) /**
Set thread character set variables from the given ID
@param thd thread handle
@param cs_number character set and collation ID
@retval 0 OK; character_set_client, collation_connection and
character_set_results are set to the new value,
or to the default global values.
@retval 1 error, e.g. the given ID is not supported by parser.
Corresponding SQL error is sent.
*/
bool thd_init_client_charset(THD *thd, uint cs_number)
{ {
CHARSET_INFO *cs;
/* /*
Use server character set and collation if Use server character set and collation if
- opt_character_set_client_handshake is not set - opt_character_set_client_handshake is not set
...@@ -380,10 +395,10 @@ void thd_init_client_charset(THD *thd, uint cs_number) ...@@ -380,10 +395,10 @@ void thd_init_client_charset(THD *thd, uint cs_number)
- client character set doesn't exists in server - client character set doesn't exists in server
*/ */
if (!opt_character_set_client_handshake || if (!opt_character_set_client_handshake ||
!(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || !(cs= get_charset(cs_number, MYF(0))) ||
!my_strcasecmp(&my_charset_latin1, !my_strcasecmp(&my_charset_latin1,
global_system_variables.character_set_client->name, global_system_variables.character_set_client->name,
thd->variables.character_set_client->name)) cs->name))
{ {
thd->variables.character_set_client= thd->variables.character_set_client=
global_system_variables.character_set_client; global_system_variables.character_set_client;
...@@ -394,10 +409,18 @@ void thd_init_client_charset(THD *thd, uint cs_number) ...@@ -394,10 +409,18 @@ void thd_init_client_charset(THD *thd, uint cs_number)
} }
else else
{ {
if (!is_supported_parser_charset(cs))
{
/* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
cs->csname);
return true;
}
thd->variables.character_set_results= thd->variables.character_set_results=
thd->variables.collation_connection= thd->variables.collation_connection=
thd->variables.character_set_client; thd->variables.character_set_client= cs;
} }
return false;
} }
......
...@@ -33,7 +33,7 @@ void reset_mqh(LEX_USER *lu, bool get_them); ...@@ -33,7 +33,7 @@ void reset_mqh(LEX_USER *lu, bool get_them);
bool check_mqh(THD *thd, uint check_command); bool check_mqh(THD *thd, uint check_command);
void time_out_user_resource_limits(THD *thd, USER_CONN *uc); void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
void decrease_user_connections(USER_CONN *uc); void decrease_user_connections(USER_CONN *uc);
void thd_init_client_charset(THD *thd, uint cs_number); bool thd_init_client_charset(THD *thd, uint cs_number);
bool setup_connection_thread_globals(THD *thd); bool setup_connection_thread_globals(THD *thd);
int check_user(THD *thd, enum enum_server_command command, int check_user(THD *thd, enum enum_server_command command,
......
...@@ -197,4 +197,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, ...@@ -197,4 +197,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
bool check_global_access(THD *thd, ulong want_access); bool check_global_access(THD *thd, ulong want_access);
inline bool is_supported_parser_charset(CHARSET_INFO *cs)
{
return test(cs->mbminlen == 1);
}
#endif /* SQL_PARSE_INCLUDED */ #endif /* SQL_PARSE_INCLUDED */
...@@ -19288,6 +19288,72 @@ static void test_bug47485() ...@@ -19288,6 +19288,72 @@ static void test_bug47485()
} }
/*
Bug#58036 client utf32, utf16, ucs2 should be disallowed, they crash server
*/
static void test_bug58036()
{
MYSQL *conn;
DBUG_ENTER("test_bug47485");
myheader("test_bug58036");
/* Part1: try to connect with ucs2 client character set */
conn= mysql_client_init(NULL);
mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2");
if (mysql_real_connect(conn, opt_host, opt_user,
opt_password, opt_db ? opt_db : "test",
opt_port, opt_unix_socket, 0))
{
if (!opt_silent)
printf("mysql_real_connect() succeeded (failure expected)\n");
mysql_close(conn);
DIE();
}
if (!opt_silent)
printf("Got mysql_real_connect() error (expected): %s (%d)\n",
mysql_error(conn), mysql_errno(conn));
DIE_UNLESS(mysql_errno(conn) == ER_WRONG_VALUE_FOR_VAR);
mysql_close(conn);
/*
Part2:
- connect with latin1
- then change client character set to ucs2
- then try mysql_change_user()
*/
conn= mysql_client_init(NULL);
mysql_options(conn, MYSQL_SET_CHARSET_NAME, "latin1");
if (!mysql_real_connect(conn, opt_host, opt_user,
opt_password, opt_db ? opt_db : "test",
opt_port, opt_unix_socket, 0))
{
if (!opt_silent)
printf("mysql_real_connect() failed: %s (%d)\n",
mysql_error(conn), mysql_errno(conn));
mysql_close(conn);
DIE();
}
mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2");
if (!mysql_change_user(conn, opt_user, opt_password, NULL))
{
if (!opt_silent)
printf("mysql_change_user() succedded, error expected!");
mysql_close(conn);
DIE();
}
if (!opt_silent)
printf("Got mysql_change_user() error (expected): %s (%d)\n",
mysql_error(conn), mysql_errno(conn));
mysql_close(conn);
DBUG_VOID_RETURN;
}
/* /*
Bug#49972: Crash in prepared statements. Bug#49972: Crash in prepared statements.
...@@ -19770,6 +19836,7 @@ static struct my_tests_st my_tests[]= { ...@@ -19770,6 +19836,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug42373", test_bug42373 }, { "test_bug42373", test_bug42373 },
{ "test_bug54041", test_bug54041 }, { "test_bug54041", test_bug54041 },
{ "test_bug47485", test_bug47485 }, { "test_bug47485", test_bug47485 },
{ "test_bug58036", test_bug58036 },
{ "test_bug57058", test_bug57058 }, { "test_bug57058", test_bug57058 },
{ 0, 0 } { 0, 0 }
}; };
......
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