1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-08 00:28:29 +03:00

MDEV-35622 SEGV, ASAN use-after-poison when reading system table with less than expected number of columns

Relaxed check, only number of columns and the PK.
Enough to avoid crashes, but doesn't break upgrades and migration
from MySQL as in MDEV-37777.

Added checks everywhere. (flush/create/alter/drop server)

Check mysql.plugin table too.
This commit is contained in:
Sergei Golubchik
2025-10-01 12:12:02 +02:00
parent bc609a8411
commit 1bdaabc0c6
5 changed files with 167 additions and 26 deletions

View File

@@ -37,11 +37,14 @@ alter server s1 options(host 'server.example.org');
rename table mysql.servers to mysql.servers_save; rename table mysql.servers to mysql.servers_save;
create table mysql.servers (x int); create table mysql.servers (x int);
alter server s1 options(host 'server.example.org'); 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'); 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; drop table mysql.servers;
rename table mysql.servers_save to mysql.servers; rename table mysql.servers_save to mysql.servers;
flush privileges;
drop server s1; drop server s1;
# #
# MDEV-35641 foreign server "disappears" after ALTERing the servers system table to use innodb and FLUSH PRIVILEGES # 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; ALTER TABLE mysql.servers ENGINE=innodb;
FLUSH PRIVILEGES; FLUSH PRIVILEGES;
drop server s1; 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

View File

@@ -34,12 +34,15 @@ create server s1 foreign data wrapper foo options(user 'a');
alter server s1 options(host 'server.example.org'); alter server s1 options(host 'server.example.org');
rename table mysql.servers to mysql.servers_save; rename table mysql.servers to mysql.servers_save;
create table mysql.servers (x int); 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'); 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'); create server s2 foreign data wrapper foo options(user 'a');
drop table mysql.servers; drop table mysql.servers;
rename table mysql.servers_save to mysql.servers; rename table mysql.servers_save to mysql.servers;
flush privileges;
drop server s1; drop server s1;
--echo # --echo #
@@ -50,3 +53,61 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1');
ALTER TABLE mysql.servers ENGINE=innodb; ALTER TABLE mysql.servers ENGINE=innodb;
FLUSH PRIVILEGES; FLUSH PRIVILEGES;
drop server s1; 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

View File

@@ -6129,10 +6129,11 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
if ((err_code= drop_server(thd, &lex->server_options))) 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", DBUG_PRINT("info", ("problem dropping server %s",
lex->server_options.server_name.str)); lex->server_options.server_name.str));
if (!thd->is_error())
my_error(err_code, MYF(0), lex->server_options.server_name.str); my_error(err_code, MYF(0), lex->server_options.server_name.str);
} }
else else

View File

@@ -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() called only by plugin_init()
*/ */
@@ -1917,6 +1930,9 @@ static void plugin_load(MEM_ROOT *tmp_root)
goto end; goto end;
} }
if (!plugin_table_is_valid(table))
goto end2;
if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0, if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
FALSE)) 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, sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno,
table->file->table_type()); table->file->table_type());
end_read_record(&read_record_info); end_read_record(&read_record_info);
end2:
table->mark_table_for_reopen(); table->mark_table_for_reopen();
close_mysql_tables(new_thd); close_mysql_tables(new_thd);
end: end:
@@ -2280,8 +2297,8 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
/* need to open before acquiring LOCK_plugin or it will deadlock */ /* need to open before acquiring LOCK_plugin or it will deadlock */
if (! (table = open_ltable(thd, &tables, TL_WRITE, table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
MYSQL_LOCK_IGNORE_TIMEOUT))) if (!table || !plugin_table_is_valid(table))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL)) 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); WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
/* need to open before acquiring LOCK_plugin or it will deadlock */ /* 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); 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 Pre-acquire audit plugins for events that may potentially occur
during [UN]INSTALL PLUGIN. during [UN]INSTALL PLUGIN.

View File

@@ -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 Forget current servers cache and read new servers
from the conneciton table. from the conneciton table.
@@ -358,6 +372,9 @@ bool servers_reload(THD *thd)
goto end; goto end;
} }
if (!servers_table_is_valid(tables.table))
goto end;
if ((return_val= servers_load(thd, &tables))) if ((return_val= servers_load(thd, &tables)))
{ // Error. Revert to old list { // Error. Revert to old list
/* blast, for now, we have no servers, discuss later way to preserve */ /* 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); 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 */ /* 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; goto end;
table->file->row_logging= 0; // Don't log to binary log table->file->row_logging= 0; // Don't log to binary log
/* insert the server into the table */ /* insert the server into the table */
@@ -559,9 +578,6 @@ store_server_fields(TABLE *table, FOREIGN_SERVER *server)
table->use_all_columns(); 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_<> "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) 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)))) if (unlikely((error= delete_server_record_in_cache(server_options))))
goto end; goto end;
if (unlikely(!(table= open_ltable(thd, &tables, TL_WRITE, table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
MYSQL_LOCK_IGNORE_TIMEOUT)))) if (!table || !servers_table_is_valid(table))
{ {
error= my_errno; error= my_errno;
goto end; 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); 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; error= my_errno;
goto end; goto end;