Skip to content

Commit 1bdaabc

Browse files
committed
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.
1 parent bc609a8 commit 1bdaabc

File tree

5 files changed

+167
-26
lines changed

5 files changed

+167
-26
lines changed

mysql-test/main/servers.result

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ alter server s1 options(host 'server.example.org');
3737
rename table mysql.servers to mysql.servers_save;
3838
create table mysql.servers (x int);
3939
alter server s1 options(host 'server.example.org');
40-
ERROR HY000: The foreign server name you are trying to reference does not exist. Data source error: s1
40+
ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted
41+
drop server s1;
42+
ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted
4143
create server s2 foreign data wrapper foo options(user 'a');
42-
ERROR HY000: Can't read record in system table
44+
ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted
4345
drop table mysql.servers;
4446
rename table mysql.servers_save to mysql.servers;
47+
flush privileges;
4548
drop server s1;
4649
#
4750
# 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');
5053
ALTER TABLE mysql.servers ENGINE=innodb;
5154
FLUSH PRIVILEGES;
5255
drop server s1;
56+
#
57+
# MDEV-35622 SEGV, ASAN use-after-poison when reading system table with less than expected number of columns
58+
# MDEV-37777 upgrade from MySQL 5.7 regression, mysql.servers invalid structure
59+
#
60+
# no crash:
61+
rename table mysql.servers to mysql.servers_save;
62+
create table mysql.servers like mysql.servers_save;
63+
alter table mysql.servers drop column owner;
64+
insert into mysql.servers values(0,0,0,0,0,0,0,0);
65+
flush privileges;
66+
ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted
67+
drop table mysql.servers;
68+
# w/o PK
69+
create table mysql.servers like mysql.servers_save;
70+
alter table mysql.servers drop primary key;
71+
insert into mysql.servers values(0,0,0,0,0,0,0,0,0);
72+
flush privileges;
73+
ERROR HY000: Cannot load from mysql.servers. The table is probably corrupted
74+
drop table mysql.servers;
75+
# upgrade is ok
76+
create table mysql.servers like mysql.servers_save;
77+
alter table mysql.servers add column Options text;
78+
create server s1 foreign data wrapper mysql options (host '127.0.0.1');
79+
flush privileges;
80+
drop server s1;
81+
drop table mysql.servers;
82+
# MySQL 5.7 (MDEV-37777)
83+
create table mysql.servers like mysql.servers_save;
84+
alter table mysql.servers modify Host char(64) not null, modify Owner char(64) not null;
85+
create server s1 foreign data wrapper mysql options (host '127.0.0.1');
86+
flush privileges;
87+
drop server s1;
88+
drop table mysql.servers;
89+
rename table mysql.servers_save to mysql.servers;
90+
# plugin
91+
create table mysql.plugin_save like mysql.plugin;
92+
alter table mysql.plugin drop column dl;
93+
install soname "ha_example";
94+
ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted
95+
uninstall soname "ha_example";
96+
ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted
97+
drop table mysql.plugin;
98+
create table mysql.plugin like mysql.plugin_save;
99+
alter table mysql.plugin drop primary key;
100+
install soname "ha_example";
101+
ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted
102+
uninstall soname "ha_example";
103+
ERROR HY000: Cannot load from mysql.plugin. The table is probably corrupted
104+
drop table mysql.plugin;
105+
rename table mysql.plugin_save to mysql.plugin;
106+
# End of 10.11 tests

mysql-test/main/servers.test

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ create server s1 foreign data wrapper foo options(user 'a');
3434
alter server s1 options(host 'server.example.org');
3535
rename table mysql.servers to mysql.servers_save;
3636
create table mysql.servers (x int);
37-
--error ER_FOREIGN_SERVER_DOESNT_EXIST
37+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
3838
alter server s1 options(host 'server.example.org');
39-
--error ER_CANT_FIND_SYSTEM_REC
39+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
40+
drop server s1;
41+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
4042
create server s2 foreign data wrapper foo options(user 'a');
4143
drop table mysql.servers;
4244
rename table mysql.servers_save to mysql.servers;
45+
flush privileges;
4346
drop server s1;
4447

4548
--echo #
@@ -50,3 +53,61 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1');
5053
ALTER TABLE mysql.servers ENGINE=innodb;
5154
FLUSH PRIVILEGES;
5255
drop server s1;
56+
57+
--echo #
58+
--echo # MDEV-35622 SEGV, ASAN use-after-poison when reading system table with less than expected number of columns
59+
--echo # MDEV-37777 upgrade from MySQL 5.7 regression, mysql.servers invalid structure
60+
--echo #
61+
62+
--echo # no crash:
63+
rename table mysql.servers to mysql.servers_save;
64+
create table mysql.servers like mysql.servers_save;
65+
alter table mysql.servers drop column owner;
66+
insert into mysql.servers values(0,0,0,0,0,0,0,0);
67+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
68+
flush privileges;
69+
drop table mysql.servers;
70+
71+
--echo # w/o PK
72+
create table mysql.servers like mysql.servers_save;
73+
alter table mysql.servers drop primary key;
74+
insert into mysql.servers values(0,0,0,0,0,0,0,0,0);
75+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
76+
flush privileges;
77+
drop table mysql.servers;
78+
79+
--echo # upgrade is ok
80+
create table mysql.servers like mysql.servers_save;
81+
alter table mysql.servers add column Options text;
82+
create server s1 foreign data wrapper mysql options (host '127.0.0.1');
83+
flush privileges;
84+
drop server s1;
85+
drop table mysql.servers;
86+
87+
--echo # MySQL 5.7 (MDEV-37777)
88+
create table mysql.servers like mysql.servers_save;
89+
alter table mysql.servers modify Host char(64) not null, modify Owner char(64) not null;
90+
create server s1 foreign data wrapper mysql options (host '127.0.0.1');
91+
flush privileges;
92+
drop server s1;
93+
drop table mysql.servers;
94+
rename table mysql.servers_save to mysql.servers;
95+
96+
--echo # plugin
97+
create table mysql.plugin_save like mysql.plugin;
98+
alter table mysql.plugin drop column dl;
99+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
100+
install soname "ha_example";
101+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
102+
uninstall soname "ha_example";
103+
drop table mysql.plugin;
104+
create table mysql.plugin like mysql.plugin_save;
105+
alter table mysql.plugin drop primary key;
106+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
107+
install soname "ha_example";
108+
--error ER_CANNOT_LOAD_FROM_TABLE_V2
109+
uninstall soname "ha_example";
110+
drop table mysql.plugin;
111+
rename table mysql.plugin_save to mysql.plugin;
112+
113+
--echo # End of 10.11 tests

sql/sql_parse.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6129,11 +6129,12 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
61296129

61306130
if ((err_code= drop_server(thd, &lex->server_options)))
61316131
{
6132-
if (! lex->if_exists() && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
6132+
if (! lex->if_exists() || err_code != ER_FOREIGN_SERVER_DOESNT_EXIST)
61336133
{
61346134
DBUG_PRINT("info", ("problem dropping server %s",
61356135
lex->server_options.server_name.str));
6136-
my_error(err_code, MYF(0), lex->server_options.server_name.str);
6136+
if (!thd->is_error())
6137+
my_error(err_code, MYF(0), lex->server_options.server_name.str);
61376138
}
61386139
else
61396140
{

sql/sql_plugin.cc

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,19 @@ static bool register_builtin(struct st_maria_plugin *plugin,
18751875
}
18761876

18771877

1878+
static bool plugin_table_is_valid(TABLE *table)
1879+
{
1880+
if (table->s->fields < PLUGIN_FIELDS_COUNT ||
1881+
table->s->primary_key == MAX_KEY)
1882+
{
1883+
my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0),
1884+
table->s->db.str, table->s->table_name.str);
1885+
return false;
1886+
}
1887+
return true;
1888+
}
1889+
1890+
18781891
/*
18791892
called only by plugin_init()
18801893
*/
@@ -1917,6 +1930,9 @@ static void plugin_load(MEM_ROOT *tmp_root)
19171930
goto end;
19181931
}
19191932

1933+
if (!plugin_table_is_valid(table))
1934+
goto end2;
1935+
19201936
if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
19211937
FALSE))
19221938
{
@@ -1978,6 +1994,7 @@ static void plugin_load(MEM_ROOT *tmp_root)
19781994
sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno,
19791995
table->file->table_type());
19801996
end_read_record(&read_record_info);
1997+
end2:
19811998
table->mark_table_for_reopen();
19821999
close_mysql_tables(new_thd);
19832000
end:
@@ -2280,8 +2297,8 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
22802297
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
22812298

22822299
/* need to open before acquiring LOCK_plugin or it will deadlock */
2283-
if (! (table = open_ltable(thd, &tables, TL_WRITE,
2284-
MYSQL_LOCK_IGNORE_TIMEOUT)))
2300+
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
2301+
if (!table || !plugin_table_is_valid(table))
22852302
DBUG_RETURN(TRUE);
22862303

22872304
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,
24382455
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
24392456

24402457
/* need to open before acquiring LOCK_plugin or it will deadlock */
2441-
if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2458+
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
2459+
if (!table || !plugin_table_is_valid(table))
24422460
DBUG_RETURN(TRUE);
24432461

2444-
if (!table->key_info)
2445-
{
2446-
my_printf_error(ER_UNKNOWN_ERROR,
2447-
"The table %s.%s has no primary key. "
2448-
"Please check the table definition and "
2449-
"create the primary key accordingly.", MYF(0),
2450-
table->s->db.str, table->s->table_name.str);
2451-
DBUG_RETURN(TRUE);
2452-
}
2453-
24542462
/*
24552463
Pre-acquire audit plugins for events that may potentially occur
24562464
during [UN]INSTALL PLUGIN.

sql/sql_servers.cc

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,20 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
315315
}
316316

317317

318+
static bool servers_table_is_valid(TABLE *table)
319+
{
320+
if (table->s->fields < SERVERS_FIELDS_COUNT ||
321+
table->s->primary_key == MAX_KEY)
322+
{
323+
my_errno= 1;
324+
my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0),
325+
table->s->db.str, table->s->table_name.str);
326+
return false;
327+
}
328+
return true;
329+
}
330+
331+
318332
/*
319333
Forget current servers cache and read new servers
320334
from the conneciton table.
@@ -358,6 +372,9 @@ bool servers_reload(THD *thd)
358372
goto end;
359373
}
360374

375+
if (!servers_table_is_valid(tables.table))
376+
goto end;
377+
361378
if ((return_val= servers_load(thd, &tables)))
362379
{ // Error. Revert to old list
363380
/* blast, for now, we have no servers, discuss later way to preserve */
@@ -479,8 +496,10 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
479496
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
480497

481498
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
482-
if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
499+
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
500+
if (!table || !servers_table_is_valid(table))
483501
goto end;
502+
484503
table->file->row_logging= 0; // Don't log to binary log
485504

486505
/* insert the server into the table */
@@ -559,9 +578,6 @@ store_server_fields(TABLE *table, FOREIGN_SERVER *server)
559578

560579
table->use_all_columns();
561580

562-
if (table->s->fields < SERVERS_FIELDS_COUNT)
563-
return ER_CANT_FIND_SYSTEM_REC;
564-
565581
/*
566582
"server" has already been prepped by prepare_server_struct_for_<>
567583
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)
711727
if (unlikely((error= delete_server_record_in_cache(server_options))))
712728
goto end;
713729

714-
if (unlikely(!(table= open_ltable(thd, &tables, TL_WRITE,
715-
MYSQL_LOCK_IGNORE_TIMEOUT))))
730+
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
731+
if (!table || !servers_table_is_valid(table))
716732
{
717733
error= my_errno;
718734
goto end;
@@ -839,7 +855,8 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
839855

840856
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
841857

842-
if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
858+
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
859+
if (!table || !servers_table_is_valid(table))
843860
{
844861
error= my_errno;
845862
goto end;

0 commit comments

Comments
 (0)