Commit 39509d64 authored by unknown's avatar unknown

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.


mysql-test/r/information_schema.result:
  Update test results (Bug#34166).
mysql-test/t/information_schema.test:
  Add a test case for Bug#34166.
sql/mysql_priv.h:
  Change signature of check_table_access() to accept a numeric limit
  of tables to check.
sql/sp_head.cc:
  Update to the new signature of check_table_access().
sql/sql_acl.cc:
  Improve code clarity: if there is a numeric limit, we should not need
  to look at first_not_own_table.
sql/sql_base.cc:
  Update to the new signature of check_table_access().
sql/sql_cache.cc:
  Update to the new signature of check_table_access().
sql/sql_parse.cc:
  Update to the new signature of check_table_access().
  Change check_table_access() to accept an optional numeric limit of tables
  to check. A crash would happen when check_table_access() was
  passed a list of tables that is not part of lex->query_tables and
  lex->query_tables_last_own was not NULL.
sql/sql_plugin.cc:
  Update to the new signature of check_table_access().
sql/sql_prepare.cc:
  Update to the new signature of check_table_access().
sql/sql_show.cc:
  Update to the new signature of check_table_access().
  Ensure that check_table_access() only checks access to the first
  table in the table list when called from list_open_tables().
  list_open_tables() supplies a table list that is created on stack,
  whereas check_table_access() used to assume that the supplied list is a part
  of thd->lex.
sql/sql_trigger.cc:
  Update to the new signature of check_table_access().
sql/sql_view.cc:
  Update to the new signature of check_table_access().
parent 97355f4e
......@@ -1619,4 +1619,19 @@ Db Name Definer Time zone Type Execute at Interval value Interval field Starts E
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
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.
......@@ -1248,4 +1248,26 @@ show events from information_schema;
show events where Db= 'information_schema';
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.
......@@ -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 no_grant, bool no_errors, bool schema_db);
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);
#else
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;
}
inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
bool no_errors)
uint number, bool no_errors)
{ return false; }
inline bool check_global_access(THD *thd, ulong want_access)
{ return false; }
......
......@@ -2265,7 +2265,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
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,
thd->security_ctx->priv_user) &&
!strcmp(sp->m_definer_host.str,
......@@ -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
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))
result= -1;
else
......
......@@ -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.
*/
for (i= 0, table= tables;
table != first_not_own_table && i < number;
i < number && table != first_not_own_table;
table= table->next_global, i++)
{
/* 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)
table_list.table_name= share->table_name.str;
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;
/* need to check if we haven't already listed it */
for (table= open_list ; table ; table=table->next)
......
......@@ -1378,7 +1378,7 @@ def_week_frmt: %lu",
table_list.db = table->db();
table_list.alias= table_list.table_name= table->table();
#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",
("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
bzero(&tables, sizeof(tables));
tables.db= (char *)"mysql";
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);
/* need to open before acquiring LOCK_plugin or it will deadlock */
......
......@@ -1272,7 +1272,7 @@ static int mysql_test_select(Prepared_statement *stmt,
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables)
{
if (check_table_access(thd, privilege, tables,0))
if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
goto error;
}
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,
THD *thd= stmt->thd;
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);
if (open_normal_and_derived_tables(thd, tables, 0))
......@@ -1374,7 +1374,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
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))
goto error;
......
......@@ -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_length= 4;
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)))
{
DBUG_RETURN(1);
......@@ -4447,10 +4447,8 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
Table_triggers_list *triggers= tables->table->triggers;
int event, timing;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_table_access(thd, TRIGGER_ACL, tables, 1))
if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
goto ret;
#endif
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)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
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;
......
......@@ -1123,8 +1123,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (!table->prelocking_placeholder &&
(old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
{
if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
check_table_access(thd, SHOW_VIEW_ACL, table, 1))
if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) &&
check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE))
{
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
goto err;
......@@ -1134,7 +1134,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
(old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
!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;
}
......
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