1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Bug#32890 Crash after repeated create and drop of tables and views

The problem is that CREATE VIEW statements inside prepared statements
weren't being expanded during the prepare phase, which leads to objects
not being allocated in the appropriate memory arenas.

The solution is to perform the validation of CREATE VIEW statements
during the prepare phase of a prepared statement. The validation
during the prepare phase assures that transformations of the parsed
tree will use the permanent arena of the prepared statement.
This commit is contained in:
davi@mysql.com/endora.local
2008-02-21 14:58:29 -03:00
parent f5cb5fdc4f
commit 0e91461842
6 changed files with 482 additions and 122 deletions

View File

@ -227,6 +227,143 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
return FALSE;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
@brief CREATE VIEW privileges pre-check.
@param thd thread handler
@param tables tables used in the view
@param views views to create
@param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
@retval FALSE Operation was a success.
@retval TRUE An error occured.
*/
bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
enum_view_create_mode mode)
{
LEX *lex= thd->lex;
/* first table in list is target VIEW name => cut off it */
TABLE_LIST *tbl;
SELECT_LEX *select_lex= &lex->select_lex;
SELECT_LEX *sl;
bool res= TRUE;
DBUG_ENTER("create_view_precheck");
/*
Privilege check for view creation:
- user has CREATE VIEW privilege on view table
- user has DROP privilege in case of ALTER VIEW or CREATE OR REPLACE
VIEW
- user has some (SELECT/UPDATE/INSERT/DELETE) privileges on columns of
underlying tables used on top of SELECT list (because it can be
(theoretically) updated, so it is enough to have UPDATE privilege on
them, for example)
- user has SELECT privilege on columns used in expressions of VIEW select
- for columns of underly tables used on top of SELECT list also will be
checked that we have not more privileges on correspondent column of view
table (i.e. user will not get some privileges by view creation)
*/
if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
(mode != VIEW_CREATE_NEW &&
(check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0))))
goto err;
for (sl= select_lex; sl; sl= sl->next_select())
{
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
{
/*
Ensure that we have some privileges on this table, more strict check
will be done on column level after preparation,
*/
if (check_some_access(thd, VIEW_ANY_ACL, tbl))
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"ANY", thd->security_ctx->priv_user,
thd->security_ctx->priv_host, tbl->table_name);
goto err;
}
/*
Mark this table as a table which will be checked after the prepare
phase
*/
tbl->table_in_first_from_clause= 1;
/*
We need to check only SELECT_ACL for all normal fields, fields for
which we need "any" (SELECT/UPDATE/INSERT/DELETE) privilege will be
checked later
*/
tbl->grant.want_privilege= SELECT_ACL;
/*
Make sure that all rights are loaded to the TABLE::grant field.
tbl->table_name will be correct name of table because VIEWs are
not opened yet.
*/
fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
tbl->table_name);
}
}
if (&lex->select_lex != lex->all_selects_list)
{
/* check tables of subqueries */
for (tbl= tables; tbl; tbl= tbl->next_global)
{
if (!tbl->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, tbl->db,
&tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
goto err;
}
}
}
/*
Mark fields for special privilege check ("any" privilege)
*/
for (sl= select_lex; sl; sl= sl->next_select())
{
List_iterator_fast<Item> it(sl->item_list);
Item *item;
while ((item= it++))
{
Item_field *field;
if ((field= item->filed_for_view_update()))
{
/*
any_privileges may be reset later by the Item_field::set_field
method in case of a system temporary table.
*/
field->any_privileges= 1;
}
}
}
res= FALSE;
err:
DBUG_RETURN(res || thd->net.report_error);
}
#else
bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
enum_view_create_mode mode)
{
return FALSE;
}
#endif
/**
@brief Creating/altering VIEW procedure
@ -241,7 +378,7 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view)
@retval TRUE An error occured.
*/
bool mysql_create_view(THD *thd, TABLE_LIST *views,
bool mysql_create_view(THD *thd, TABLE_LIST *views,
enum_view_create_mode mode)
{
LEX *lex= thd->lex;
@ -325,109 +462,11 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
}
}
/*
Privilege check for view creation:
- user has CREATE VIEW privilege on view table
- user has DROP privilege in case of ALTER VIEW or CREATE OR REPLACE
VIEW
- user has some (SELECT/UPDATE/INSERT/DELETE) privileges on columns of
underlying tables used on top of SELECT list (because it can be
(theoretically) updated, so it is enough to have UPDATE privilege on
them, for example)
- user has SELECT privilege on columns used in expressions of VIEW select
- for columns of underly tables used on top of SELECT list also will be
checked that we have not more privileges on correspondent column of view
table (i.e. user will not get some privileges by view creation)
*/
if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
(mode != VIEW_CREATE_NEW &&
(check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0))))
{
res= TRUE;
goto err;
}
for (sl= select_lex; sl; sl= sl->next_select())
{
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
{
/*
Ensure that we have some privileges on this table, more strict check
will be done on column level after preparation,
*/
if (check_some_access(thd, VIEW_ANY_ACL, tbl))
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"ANY", thd->security_ctx->priv_user,
thd->security_ctx->priv_host, tbl->table_name);
res= TRUE;
goto err;
}
/*
Mark this table as a table which will be checked after the prepare
phase
*/
tbl->table_in_first_from_clause= 1;
/*
We need to check only SELECT_ACL for all normal fields, fields for
which we need "any" (SELECT/UPDATE/INSERT/DELETE) privilege will be
checked later
*/
tbl->grant.want_privilege= SELECT_ACL;
/*
Make sure that all rights are loaded to the TABLE::grant field.
tbl->table_name will be correct name of table because VIEWs are
not opened yet.
*/
fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
tbl->table_name);
}
}
if (&lex->select_lex != lex->all_selects_list)
{
/* check tables of subqueries */
for (tbl= tables; tbl; tbl= tbl->next_global)
{
if (!tbl->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, tbl->db,
&tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
{
res= TRUE;
goto err;
}
}
}
}
/*
Mark fields for special privilege check ("any" privilege)
*/
for (sl= select_lex; sl; sl= sl->next_select())
{
List_iterator_fast<Item> it(sl->item_list);
Item *item;
while ((item= it++))
{
Item_field *field;
if ((field= item->filed_for_view_update()))
{
/*
any_privileges may be reset later by the Item_field::set_field
method in case of a system temporary table.
*/
field->any_privileges= 1;
}
}
}
#endif
if ((res= create_view_precheck(thd, tables, view, mode)))
goto err;
if (open_and_lock_tables(thd, tables))
{
res= TRUE;