From e4090f813a36c21971dd584eea94e8e5a66d139e Mon Sep 17 00:00:00 2001 From: Alexander Barkov <alexander.barkov@oracle.com> Date: Fri, 18 Feb 2011 16:12:36 +0300 Subject: [PATCH] Bug#11765108 (Bug#58036) client utf32, utf16, ucs2 should be disallowed, they crash server A separate fix for 5.1 (as 5.1 and 5.5 have seriously differged in the related pieces of the code). A patch for 5.5 was approved earlier. 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/sql_priv.h - changing return type for thd_init_client_charset() to bool, to return errors to the caller @ sql/sql_var.cc - using new function @ sql/sql_connect.cc - thd_client_charset_init: in case of unsupported client character set send error and return true; in case of success return false - check_connection: Return error if character set initialization failed @ sql/sql_parse.cc - check charset in the very beginnig of the CMD_CHANGE_USER handling code @ tests/mysql_client_test.c - adding tests --- sql/mysql_priv.h | 6 +++- sql/set_var.cc | 2 +- sql/sql_connect.cc | 34 +++++++++++++++++--- sql/sql_parse.cc | 17 ++++++++-- tests/mysql_client_test.c | 67 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 9 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1838f0c924d..67631b265ab 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1019,7 +1019,11 @@ void reset_mqh(LEX_USER *lu, bool get_them); bool check_mqh(THD *thd, uint check_command); void time_out_user_resource_limits(THD *thd, 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); +inline bool is_supported_parser_charset(CHARSET_INFO *cs) +{ + return test(cs->mbminlen == 1); +} bool setup_connection_thread_globals(THD *thd); int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); diff --git a/sql/set_var.cc b/sql/set_var.cc index 26c9b06a912..831b68bbe14 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2187,7 +2187,7 @@ bool sys_var_character_set_client::check(THD *thd, set_var *var) if (sys_var_character_set_sv::check(thd, var)) return 1; /* Currently, UCS-2 cannot be used as a client character set */ - if (var->save_result.charset->mbminlen > 1) + if (!is_supported_parser_charset(var->save_result.charset)) { my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, var->save_result.charset->csname); diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 9fa6966baa2..8129324e300 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -582,8 +582,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 - opt_character_set_client_handshake is not set @@ -592,10 +607,10 @@ void thd_init_client_charset(THD *thd, uint cs_number) - client character set doesn't exists in server */ 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, global_system_variables.character_set_client->name, - thd->variables.character_set_client->name)) + cs->name)) { thd->variables.character_set_client= global_system_variables.character_set_client; @@ -606,10 +621,18 @@ void thd_init_client_charset(THD *thd, uint cs_number) } 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.collation_connection= - thd->variables.character_set_client; + thd->variables.character_set_client= cs; } + return false; } @@ -782,7 +805,8 @@ static int check_connection(THD *thd) thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; thd->max_client_packet_length= uint4korr(net->read_pos+4); DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - thd_init_client_charset(thd, (uint) net->read_pos[8]); + if (thd_init_client_charset(thd, (uint) net->read_pos[8])) + return 1; thd->update_charset(); end= (char*) net->read_pos+32; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6ce800312ff..9bf55f4dcdd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1153,13 +1153,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (ptr < packet_end) { + CHARSET_INFO *cs; if (ptr + 2 > packet_end) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - cs_number= uint2korr(ptr); + if ((cs_number= uint2korr(ptr)) && + (cs= get_charset(cs_number, MYF(0))) && + !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); + break; + } } /* Convert database name to utf8 */ @@ -1205,7 +1214,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (cs_number) { - thd_init_client_charset(thd, cs_number); + /* + We have checked charset earlier, + so thd_init_client_charset cannot fail. + */ + DBUG_ASSERT(!thd_init_client_charset(thd, cs_number)); thd->update_charset(); } } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 80c7be64e94..5a45c132a5f 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18398,6 +18398,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; +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -18724,6 +18790,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug58036", test_bug58036 }, { 0, 0 } }; -- 2.30.9