Skip to content

Commit d9b1ac0

Browse files
author
Arun Kuruvila
committed
Bug #20857652 : SERVER CRASHES WITH CREATE/DROP USER
STATEMENT WITH A PARTICULAR SEQUENCE Description :- Server crashes with CREATE/DROP USER statements after a failed FLUSH PRIVILEGES statement. Analysis :- When FLUSH PRIVILEGES fails because of the failure of "open_and_lock_tables()" in "acl_reload()", the ACL structures are freed. After this server crashes with CREATE/DROP USER statements when it is trying to access the freed ACL structures. Fix :- ACL structures are retained even if FLUSH PRIVILEGES fails in reloading ACL privileges. Similar behaviour is done while reloading column, function and procedure privileges. In the case of reloading function and procedure privileges we come across with the following scenarios:- 1) mysql.procs_priv table is not present and proc_priv_hash structure has been already initialized=> mysql.procs_priv table has been dropped/renamed Soln:- An error, "ERROR 1146 (42S02): Table 'mysql.procs_priv' doesn't exist", is thrown. 2) mysql.procs_priv table is not present and proc_priv_hash structure is not yet initialized => a) We are using a pre 4.1 system tables. or b) We dropped/renamed mysql.proc_priv table and shutdown the server and restarted the server again Soln:- A warning, "[Warning] Table 'mysql.procs_priv' does not exist. Please run mysql_upgrade." is printed in the server log and a warning is thrown to the client side as well. Continue with reloading column privileges. 3) mysql.procs_priv table is present => Normal scenario. Column, function and procedure privileges are reloaded as normal.
1 parent 7b6899b commit d9b1ac0

File tree

3 files changed

+62
-37
lines changed

3 files changed

+62
-37
lines changed

mysql-test/r/grant.result

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,7 @@ revoke create, insert on mysqltest.t6 from mysqltest@localhost;
16961696
drop user mysqltest@localhost;
16971697
drop database mysqltest;
16981698
use test;
1699+
call mtr.add_suppression("Can't open and lock privilege tables");
16991700
FLUSH PRIVILEGES without procs_priv table.
17001701
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
17011702
FLUSH PRIVILEGES;

mysql-test/t/grant.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,9 @@ use test;
16691669
#
16701670
# Bug#16470 crash on grant if old grant tables
16711671
#
1672+
1673+
call mtr.add_suppression("Can't open and lock privilege tables");
1674+
16721675
--echo FLUSH PRIVILEGES without procs_priv table.
16731676
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
16741677
--error ER_NO_SUCH_TABLE

sql/sql_acl.cc

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,6 @@ my_bool acl_reload(THD *thd)
14221422
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
14231423
thd->get_stmt_da()->message());
14241424
}
1425-
acl_free();
14261425
goto end;
14271426
}
14281427

@@ -5422,6 +5421,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
54225421
exists.
54235422
54245423
@param thd A pointer to the thread handler object.
5424+
@param table A pointer to the table list.
54255425
54265426
@see grant_reload
54275427
@@ -5430,35 +5430,22 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
54305430
@retval TRUE An error has occurred.
54315431
*/
54325432

5433-
static my_bool grant_reload_procs_priv(THD *thd)
5433+
static my_bool grant_reload_procs_priv(THD *thd, TABLE_LIST *table)
54345434
{
54355435
HASH old_proc_priv_hash, old_func_priv_hash;
5436-
TABLE_LIST table;
54375436
my_bool return_val= FALSE;
54385437
DBUG_ENTER("grant_reload_procs_priv");
54395438

5440-
table.init_one_table("mysql", 5, "procs_priv",
5441-
strlen("procs_priv"), "procs_priv",
5442-
TL_READ);
5443-
table.open_type= OT_BASE_ONLY;
5444-
5445-
if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5446-
{
5447-
my_hash_free(&proc_priv_hash);
5448-
my_hash_free(&func_priv_hash);
5449-
DBUG_RETURN(TRUE);
5450-
}
5451-
5452-
mysql_rwlock_wrlock(&LOCK_grant);
54535439
/* Save a copy of the current hash if we need to undo the grant load */
54545440
old_proc_priv_hash= proc_priv_hash;
54555441
old_func_priv_hash= func_priv_hash;
54565442

5457-
if ((return_val= grant_load_procs_priv(table.table)))
5443+
if ((return_val= grant_load_procs_priv(table->table)))
54585444
{
54595445
/* Error; Reverting to old hash */
54605446
DBUG_PRINT("error",("Reverting to old privileges"));
5461-
grant_free();
5447+
my_hash_free(&proc_priv_hash);
5448+
my_hash_free(&func_priv_hash);
54625449
proc_priv_hash= old_proc_priv_hash;
54635450
func_priv_hash= old_func_priv_hash;
54645451
}
@@ -5467,7 +5454,6 @@ static my_bool grant_reload_procs_priv(THD *thd)
54675454
my_hash_free(&old_proc_priv_hash);
54685455
my_hash_free(&old_func_priv_hash);
54695456
}
5470-
mysql_rwlock_unlock(&LOCK_grant);
54715457

54725458
DBUG_RETURN(return_val);
54735459
}
@@ -5490,7 +5476,7 @@ static my_bool grant_reload_procs_priv(THD *thd)
54905476

54915477
my_bool grant_reload(THD *thd)
54925478
{
5493-
TABLE_LIST tables[2];
5479+
TABLE_LIST tables[3];
54945480
HASH old_column_priv_hash;
54955481
MEM_ROOT old_mem;
54965482
my_bool return_val= 1;
@@ -5506,19 +5492,58 @@ my_bool grant_reload(THD *thd)
55065492
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
55075493
C_STRING_WITH_LEN("columns_priv"),
55085494
"columns_priv", TL_READ);
5495+
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
5496+
C_STRING_WITH_LEN("procs_priv"),
5497+
"procs_priv", TL_READ);
5498+
55095499
tables[0].next_local= tables[0].next_global= tables+1;
5510-
tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
5500+
tables[1].next_local= tables[1].next_global= tables+2;
5501+
tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
5502+
5503+
/*
5504+
Reload will work in the following manner:-
5505+
5506+
proc_priv_hash structure
5507+
/ \
5508+
not initialized initialized
5509+
/ \ |
5510+
mysql.procs_priv table Server Startup |
5511+
is missing \ |
5512+
| open_and_lock_tables()
5513+
Assume we are working on /success \failure
5514+
pre 4.1 system tables. Normal Scenario. An error is thrown.
5515+
A warning is printed Reload column privilege. Retain the old hash.
5516+
and continue with Reload function and
5517+
reloading the column procedure privileges,
5518+
privileges. if available.
5519+
*/
5520+
5521+
if (!(my_hash_inited(&proc_priv_hash)))
5522+
tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
55115523

55125524
/*
55135525
To avoid deadlocks we should obtain table locks before
55145526
obtaining LOCK_grant rwlock.
55155527
*/
55165528
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
55175529
{
5518-
my_hash_free(&column_priv_hash);
5530+
if (thd->get_stmt_da()->is_error())
5531+
{
5532+
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
5533+
thd->get_stmt_da()->message());
5534+
}
55195535
goto end;
55205536
}
55215537

5538+
if (tables[2].table == NULL)
5539+
{
5540+
sql_print_warning("Table 'mysql.procs_priv' does not exist. "
5541+
"Please run mysql_upgrade.");
5542+
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_SUCH_TABLE,
5543+
ER(ER_NO_SUCH_TABLE), tables[2].db,
5544+
tables[2].table_name);
5545+
}
5546+
55225547
mysql_rwlock_wrlock(&LOCK_grant);
55235548
old_column_priv_hash= column_priv_hash;
55245549

@@ -5528,32 +5553,28 @@ my_bool grant_reload(THD *thd)
55285553
*/
55295554
old_mem= memex;
55305555
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
5531-
5532-
if ((return_val= grant_load(thd, tables)))
5556+
/*
5557+
tables[2].table i.e. procs_priv can be null if we are working with
5558+
pre 4.1 privilage tables
5559+
*/
5560+
if ((return_val= grant_load(thd, tables)) ||
5561+
(tables[2].table != NULL &&
5562+
grant_reload_procs_priv(thd, &tables[2]))
5563+
)
55335564
{ // Error. Revert to old hash
55345565
DBUG_PRINT("error",("Reverting to old privileges"));
5535-
grant_free(); /* purecov: deadcode */
5566+
my_hash_free(&column_priv_hash);
5567+
free_root(&memex,MYF(0));
55365568
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
55375569
memex= old_mem; /* purecov: deadcode */
55385570
}
55395571
else
55405572
{
55415573
my_hash_free(&old_column_priv_hash);
55425574
free_root(&old_mem,MYF(0));
5575+
grant_version++;
55435576
}
55445577
mysql_rwlock_unlock(&LOCK_grant);
5545-
close_acl_tables(thd);
5546-
5547-
/*
5548-
It is OK failing to load procs_priv table because we may be
5549-
working with 4.1 privilege tables.
5550-
*/
5551-
if (grant_reload_procs_priv(thd))
5552-
return_val= 1;
5553-
5554-
mysql_rwlock_wrlock(&LOCK_grant);
5555-
grant_version++;
5556-
mysql_rwlock_unlock(&LOCK_grant);
55575578

55585579
end:
55595580
close_acl_tables(thd);

0 commit comments

Comments
 (0)