Commit b46ce809 authored by kostja@dipika.(none)'s avatar kostja@dipika.(none)

A fix and a test case for Bug#34166 Server crash in SHOW OPEN TABLES and

pre-locking.

The crash was caused by an implicit assumption in check_table_access() that
table_list parameter is always a part of lex->query_tables.

When iterating over the passed list of tables, check_table_access() used
to stop only when lex->query_tables_last_not_own was reached. 
In case of pre-locking, lex->query_tables_last_own is not NULL and points
to some element of lex->query_tables. When the parameter
of check_table_access() was not part of lex->query_tables, loop invariant
could never be violated and a crash would happen when the current table
pointer would point beyond the end of the provided list.

The fix is to change the signature of check_table_access() to also accept
a numeric limit of loop iterations, similarly to check_grant(), and 
supply this limit in all places when we want to check access of tables
that are outside lex->query_tables, or just want to check access to one table.
parent 960c2434
...@@ -1619,4 +1619,19 @@ Db Name Definer Time zone Type Execute at Interval value Interval field Starts E ...@@ -1619,4 +1619,19 @@ Db Name Definer Time zone Type Execute at Interval value Interval field Starts E
show events where Db= 'information_schema'; show events where Db= 'information_schema';
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
use test; use test;
#
# Bug#34166: Server crash in SHOW OPEN TABLES and prelocking
#
drop table if exists t1;
drop function if exists f1;
create table t1 (a int);
create function f1() returns int
begin
insert into t1 (a) values (1);
return 0;
end|
show open tables where f1()=0;
show open tables where f1()=0;
drop table t1;
drop function f1;
End of 5.1 tests. End of 5.1 tests.
...@@ -1248,4 +1248,26 @@ show events from information_schema; ...@@ -1248,4 +1248,26 @@ show events from information_schema;
show events where Db= 'information_schema'; show events where Db= 'information_schema';
use test; use test;
--echo #
--echo # Bug#34166: Server crash in SHOW OPEN TABLES and prelocking
--echo #
--disable_warnings
drop table if exists t1;
drop function if exists f1;
--enable_warnings
create table t1 (a int);
delimiter |;
create function f1() returns int
begin
insert into t1 (a) values (1);
return 0;
end|
delimiter ;|
--disable_result_log
show open tables where f1()=0;
show open tables where f1()=0;
--enable_result_log
drop table t1;
drop function f1;
--echo End of 5.1 tests. --echo End of 5.1 tests.
...@@ -1015,7 +1015,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, ...@@ -1015,7 +1015,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
bool no_grant, bool no_errors, bool schema_db); bool no_grant, bool no_errors, bool schema_db);
bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
bool no_errors); uint number, bool no_errors);
bool check_global_access(THD *thd, ulong want_access); bool check_global_access(THD *thd, ulong want_access);
#else #else
inline bool check_access(THD *thd, ulong access, const char *db, inline bool check_access(THD *thd, ulong access, const char *db,
...@@ -1027,7 +1027,7 @@ inline bool check_access(THD *thd, ulong access, const char *db, ...@@ -1027,7 +1027,7 @@ inline bool check_access(THD *thd, ulong access, const char *db,
return false; return false;
} }
inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
bool no_errors) uint number, bool no_errors)
{ return false; } { return false; }
inline bool check_global_access(THD *thd, ulong want_access) inline bool check_global_access(THD *thd, ulong want_access)
{ return false; } { return false; }
......
...@@ -2265,7 +2265,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) ...@@ -2265,7 +2265,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables)); bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql"; tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "proc"; tables.table_name= tables.alias= (char*) "proc";
*full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) || *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) ||
(!strcmp(sp->m_definer_user.str, (!strcmp(sp->m_definer_user.str,
thd->security_ctx->priv_user) && thd->security_ctx->priv_user) &&
!strcmp(sp->m_definer_host.str, !strcmp(sp->m_definer_host.str,
...@@ -2712,7 +2712,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2712,7 +2712,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Check whenever we have access to tables for this statement Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function. and open and lock them before executing instructions core function.
*/ */
if (check_table_access(thd, SELECT_ACL, tables, 0) if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, tables)) || open_and_lock_tables(thd, tables))
result= -1; result= -1;
else else
......
...@@ -3862,7 +3862,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ...@@ -3862,7 +3862,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
of other queries). For simple queries first_not_own_table is 0. of other queries). For simple queries first_not_own_table is 0.
*/ */
for (i= 0, table= tables; for (i= 0, table= tables;
table != first_not_own_table && i < number; i < number && table != first_not_own_table;
table= table->next_global, i++) table= table->next_global, i++)
{ {
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */ /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
......
...@@ -799,7 +799,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) ...@@ -799,7 +799,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
table_list.table_name= share->table_name.str; table_list.table_name= share->table_name.str;
table_list.grant.privilege=0; table_list.grant.privilege=0;
if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE))
continue; continue;
/* need to check if we haven't already listed it */ /* need to check if we haven't already listed it */
for (table= open_list ; table ; table=table->next) for (table= open_list ; table ; table=table->next)
......
...@@ -1378,7 +1378,7 @@ def_week_frmt: %lu", ...@@ -1378,7 +1378,7 @@ def_week_frmt: %lu",
table_list.db = table->db(); table_list.db = table->db();
table_list.alias= table_list.table_name= table->table(); table_list.alias= table_list.table_name= table->table();
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_table_access(thd,SELECT_ACL,&table_list,1)) if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE))
{ {
DBUG_PRINT("qcache", DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing", ("probably no SELECT access to %s.%s => return to normal processing",
......
This diff is collapsed.
...@@ -1619,7 +1619,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl ...@@ -1619,7 +1619,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
bzero(&tables, sizeof(tables)); bzero(&tables, sizeof(tables));
tables.db= (char *)"mysql"; tables.db= (char *)"mysql";
tables.table_name= tables.alias= (char *)"plugin"; tables.table_name= tables.alias= (char *)"plugin";
if (check_table_access(thd, INSERT_ACL, &tables, 0)) if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */ /* need to open before acquiring LOCK_plugin or it will deadlock */
......
...@@ -1272,7 +1272,7 @@ static int mysql_test_select(Prepared_statement *stmt, ...@@ -1272,7 +1272,7 @@ static int mysql_test_select(Prepared_statement *stmt,
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables) if (tables)
{ {
if (check_table_access(thd, privilege, tables,0)) if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
goto error; goto error;
} }
else if (check_access(thd, privilege, any_db,0,0,0,0)) else if (check_access(thd, privilege, any_db,0,0,0,0))
...@@ -1342,7 +1342,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, ...@@ -1342,7 +1342,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt,
THD *thd= stmt->thd; THD *thd= stmt->thd;
DBUG_ENTER("mysql_test_do_fields"); DBUG_ENTER("mysql_test_do_fields");
if (tables && check_table_access(thd, SELECT_ACL, tables, 0)) if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (open_normal_and_derived_tables(thd, tables, 0)) if (open_normal_and_derived_tables(thd, tables, 0))
...@@ -1374,7 +1374,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, ...@@ -1374,7 +1374,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
THD *thd= stmt->thd; THD *thd= stmt->thd;
set_var_base *var; set_var_base *var;
if (tables && check_table_access(thd, SELECT_ACL, tables, 0) || if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) ||
open_normal_and_derived_tables(thd, tables, 0)) open_normal_and_derived_tables(thd, tables, 0))
goto error; goto error;
......
...@@ -4059,7 +4059,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -4059,7 +4059,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name= proc_tables.alias= (char*) "proc"; proc_tables.table_name= proc_tables.alias= (char*) "proc";
proc_tables.table_name_length= 4; proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ; proc_tables.lock_type= TL_READ;
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1); full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE);
if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{ {
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -4447,10 +4447,8 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, ...@@ -4447,10 +4447,8 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
Table_triggers_list *triggers= tables->table->triggers; Table_triggers_list *triggers= tables->table->triggers;
int event, timing; int event, timing;
#ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
if (check_table_access(thd, TRIGGER_ACL, tables, 1))
goto ret; goto ret;
#endif
for (event= 0; event < (int)TRG_EVENT_MAX; event++) for (event= 0; event < (int)TRG_EVENT_MAX; event++)
{ {
......
...@@ -418,7 +418,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -418,7 +418,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
thd->lex->query_tables_own_last= 0; thd->lex->query_tables_own_last= 0;
err_status= check_table_access(thd, TRIGGER_ACL, tables, 0); err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE);
thd->lex->query_tables_own_last= save_query_tables_own_last; thd->lex->query_tables_own_last= save_query_tables_own_last;
......
...@@ -1123,8 +1123,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1123,8 +1123,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (!table->prelocking_placeholder && if (!table->prelocking_placeholder &&
(old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)) (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
{ {
if (check_table_access(thd, SELECT_ACL, view_tables, 1) && if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) &&
check_table_access(thd, SHOW_VIEW_ACL, table, 1)) check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE))
{ {
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
goto err; goto err;
...@@ -1134,7 +1134,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1134,7 +1134,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
(old_lex->sql_command == SQLCOM_SHOW_CREATE) && (old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
!table->belong_to_view) !table->belong_to_view)
{ {
if (check_table_access(thd, SHOW_VIEW_ACL, table, 0)) if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE))
goto err; goto err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment