diff --git a/mysql-test/main/servers.result b/mysql-test/main/servers.result index a02c3d6ac74..1460eefaa30 100644 --- a/mysql-test/main/servers.result +++ b/mysql-test/main/servers.result @@ -37,11 +37,14 @@ alter server s1 options(host 'server.example.org'); rename table mysql.servers to mysql.servers_save; create table mysql.servers (x int); alter server s1 options(host 'server.example.org'); -ERROR HY000: The foreign server name you are trying to reference does not exist. Data source error: s1 +ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted +drop server s1; +ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted create server s2 foreign data wrapper foo options(user 'a'); -ERROR HY000: Can't read record in system table +ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted drop table mysql.servers; rename table mysql.servers_save to mysql.servers; +flush privileges; drop server s1; # # MDEV-35641 foreign server "disappears" after ALTERing the servers system table to use innodb and FLUSH PRIVILEGES @@ -50,3 +53,54 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1'); ALTER TABLE mysql.servers ENGINE=innodb; FLUSH PRIVILEGES; drop server s1; +# +# MDEV-35622 SEGV, ASAN use-after-poison when reading system table with less than expected number of columns +# MDEV-37777 upgrade from MySQL 5.7 regression, mysql.servers invalid structure +# +# no crash: +rename table mysql.servers to mysql.servers_save; +create table mysql.servers like mysql.servers_save; +alter table mysql.servers drop column owner; +insert into mysql.servers values(0,0,0,0,0,0,0,0); +flush privileges; +ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted +drop table mysql.servers; +# w/o PK +create table mysql.servers like mysql.servers_save; +alter table mysql.servers drop primary key; +insert into mysql.servers values(0,0,0,0,0,0,0,0,0); +flush privileges; +ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted +drop table mysql.servers; +# upgrade is ok +create table mysql.servers like mysql.servers_save; +alter table mysql.servers add column Options text; +create server s1 foreign data wrapper mysql options (host '127.0.0.1'); +flush privileges; +drop server s1; +drop table mysql.servers; +# MySQL 5.7 (MDEV-37777) +create table mysql.servers like mysql.servers_save; +alter table mysql.servers modify Host char(64) not null, modify Owner char(64) not null; +create server s1 foreign data wrapper mysql options (host '127.0.0.1'); +flush privileges; +drop server s1; +drop table mysql.servers; +rename table mysql.servers_save to mysql.servers; +# plugin +create table mysql.plugin_save like mysql.plugin; +alter table mysql.plugin drop column dl; +install soname "ha_example"; +ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted +uninstall soname "ha_example"; +ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted +drop table mysql.plugin; +create table mysql.plugin like mysql.plugin_save; +alter table mysql.plugin drop primary key; +install soname "ha_example"; +ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted +uninstall soname "ha_example"; +ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted +drop table mysql.plugin; +rename table mysql.plugin_save to mysql.plugin; +# End of 10.11 tests diff --git a/mysql-test/main/servers.test b/mysql-test/main/servers.test index f46041bb21c..5ef82173e88 100644 --- a/mysql-test/main/servers.test +++ b/mysql-test/main/servers.test @@ -34,12 +34,15 @@ create server s1 foreign data wrapper foo options(user 'a'); alter server s1 options(host 'server.example.org'); rename table mysql.servers to mysql.servers_save; create table mysql.servers (x int); ---error ER_FOREIGN_SERVER_DOESNT_EXIST +--error ER_CANNOT_LOAD_FROM_TABLE_V2 alter server s1 options(host 'server.example.org'); ---error ER_CANT_FIND_SYSTEM_REC +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +drop server s1; +--error ER_CANNOT_LOAD_FROM_TABLE_V2 create server s2 foreign data wrapper foo options(user 'a'); drop table mysql.servers; rename table mysql.servers_save to mysql.servers; +flush privileges; drop server s1; --echo # @@ -50,3 +53,61 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1'); ALTER TABLE mysql.servers ENGINE=innodb; FLUSH PRIVILEGES; drop server s1; + +--echo # +--echo # MDEV-35622 SEGV, ASAN use-after-poison when reading system table with less than expected number of columns +--echo # MDEV-37777 upgrade from MySQL 5.7 regression, mysql.servers invalid structure +--echo # + +--echo # no crash: +rename table mysql.servers to mysql.servers_save; +create table mysql.servers like mysql.servers_save; +alter table mysql.servers drop column owner; +insert into mysql.servers values(0,0,0,0,0,0,0,0); +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +flush privileges; +drop table mysql.servers; + +--echo # w/o PK +create table mysql.servers like mysql.servers_save; +alter table mysql.servers drop primary key; +insert into mysql.servers values(0,0,0,0,0,0,0,0,0); +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +flush privileges; +drop table mysql.servers; + +--echo # upgrade is ok +create table mysql.servers like mysql.servers_save; +alter table mysql.servers add column Options text; +create server s1 foreign data wrapper mysql options (host '127.0.0.1'); +flush privileges; +drop server s1; +drop table mysql.servers; + +--echo # MySQL 5.7 (MDEV-37777) +create table mysql.servers like mysql.servers_save; +alter table mysql.servers modify Host char(64) not null, modify Owner char(64) not null; +create server s1 foreign data wrapper mysql options (host '127.0.0.1'); +flush privileges; +drop server s1; +drop table mysql.servers; +rename table mysql.servers_save to mysql.servers; + +--echo # plugin +create table mysql.plugin_save like mysql.plugin; +alter table mysql.plugin drop column dl; +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +install soname "ha_example"; +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +uninstall soname "ha_example"; +drop table mysql.plugin; +create table mysql.plugin like mysql.plugin_save; +alter table mysql.plugin drop primary key; +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +install soname "ha_example"; +--error ER_CANNOT_LOAD_FROM_TABLE_V2 +uninstall soname "ha_example"; +drop table mysql.plugin; +rename table mysql.plugin_save to mysql.plugin; + +--echo # End of 10.11 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 007af961396..a66e1ec83c1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6129,11 +6129,12 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) if ((err_code= drop_server(thd, &lex->server_options))) { - if (! lex->if_exists() && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST) + if (! lex->if_exists() || err_code != ER_FOREIGN_SERVER_DOESNT_EXIST) { DBUG_PRINT("info", ("problem dropping server %s", lex->server_options.server_name.str)); - my_error(err_code, MYF(0), lex->server_options.server_name.str); + if (!thd->is_error()) + my_error(err_code, MYF(0), lex->server_options.server_name.str); } else { diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 35319c4c2eb..1239d165fc7 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1875,6 +1875,19 @@ static bool register_builtin(struct st_maria_plugin *plugin, } +static bool plugin_table_is_valid(TABLE *table) +{ + if (table->s->fields < PLUGIN_FIELDS_COUNT || + table->s->primary_key == MAX_KEY) + { + my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), + table->s->db.str, table->s->table_name.str); + return false; + } + return true; +} + + /* called only by plugin_init() */ @@ -1917,6 +1930,9 @@ static void plugin_load(MEM_ROOT *tmp_root) goto end; } + if (!plugin_table_is_valid(table)) + goto end2; + if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0, FALSE)) { @@ -1978,6 +1994,7 @@ static void plugin_load(MEM_ROOT *tmp_root) sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno, table->file->table_type()); end_read_record(&read_record_info); +end2: table->mark_table_for_reopen(); close_mysql_tables(new_thd); end: @@ -2280,8 +2297,8 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table = open_ltable(thd, &tables, TL_WRITE, - MYSQL_LOCK_IGNORE_TIMEOUT))) + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + if (!table || !plugin_table_is_valid(table)) DBUG_RETURN(TRUE); if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL)) @@ -2438,19 +2455,10 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + if (!table || !plugin_table_is_valid(table)) DBUG_RETURN(TRUE); - if (!table->key_info) - { - my_printf_error(ER_UNKNOWN_ERROR, - "The table %s.%s has no primary key. " - "Please check the table definition and " - "create the primary key accordingly.", MYF(0), - table->s->db.str, table->s->table_name.str); - DBUG_RETURN(TRUE); - } - /* Pre-acquire audit plugins for events that may potentially occur during [UN]INSTALL PLUGIN. diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 3fa319eb571..c6e06ca3d48 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -315,6 +315,20 @@ end: } +static bool servers_table_is_valid(TABLE *table) +{ + if (table->s->fields < SERVERS_FIELDS_COUNT || + table->s->primary_key == MAX_KEY) + { + my_errno= 1; + my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), + table->s->db.str, table->s->table_name.str); + return false; + } + return true; +} + + /* Forget current servers cache and read new servers from the conneciton table. @@ -358,6 +372,9 @@ bool servers_reload(THD *thd) goto end; } + if (!servers_table_is_valid(tables.table)) + goto end; + if ((return_val= servers_load(thd, &tables))) { // Error. Revert to old list /* blast, for now, we have no servers, discuss later way to preserve */ @@ -479,8 +496,10 @@ insert_server(THD *thd, FOREIGN_SERVER *server) tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ - if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + if (!table || !servers_table_is_valid(table)) goto end; + table->file->row_logging= 0; // Don't log to binary log /* insert the server into the table */ @@ -559,9 +578,6 @@ store_server_fields(TABLE *table, FOREIGN_SERVER *server) table->use_all_columns(); - if (table->s->fields < SERVERS_FIELDS_COUNT) - return ER_CANT_FIND_SYSTEM_REC; - /* "server" has already been prepped by prepare_server_struct_for_<> so, all we need to do is check if the value is set (> -1 for port) @@ -711,8 +727,8 @@ static int drop_server_internal(THD *thd, LEX_SERVER_OPTIONS *server_options) if (unlikely((error= delete_server_record_in_cache(server_options)))) goto end; - if (unlikely(!(table= open_ltable(thd, &tables, TL_WRITE, - MYSQL_LOCK_IGNORE_TIMEOUT)))) + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + if (!table || !servers_table_is_valid(table)) { error= my_errno; goto end; @@ -839,7 +855,8 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE); - if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + if (!table || !servers_table_is_valid(table)) { error= my_errno; goto end;