Commit 361977c0 authored by unknown's avatar unknown
Browse files

Fix for bug #11555 "Stored procedures: current SP tables locking make

impossible view security".

We should not expose names of tables which are explicitly or implicitly (via
routine or trigger) used by view even if we find that they are missing.
So during building of list of prelocked tables for statement we track which
routines (and therefore tables for these routines) are used from views. We
mark elements of LEX::routines set which correspond to routines used in views
by setting Sroutine_hash_entry::belong_to_view member to point to TABLE_LIST
object for topmost view which uses routine. We propagate this mark to all
routines which are used by this routine and which we add to this set. We also
mark tables used by such routine which we add to the list of tables for
prelocking as belonging to this view.


mysql-test/r/sp-error.result:
  Added test for bug #11555 "Stored procedures: current SP tables locking make 
  impossible view security".
mysql-test/r/view.result:
  We should not expose tables which are expicitly/implicitly used in view in
  check table statement.
mysql-test/t/sp-error.test:
  Added test for bug #11555 "Stored procedures: current SP tables locking make 
  impossible view security".
mysql-test/t/view.test:
  Removed comment obsoleted by bugfix.
sql/sp.cc:
  We should not expose names of tables which are explicitly or implicitly
  (via routine or trigger) used by view even if we find that they are missing.
  So during building of list of prelocked tables for statement we track which
  routines (and therefore tables for these routines) are used from views. We
  mark elements of LEX::routines set which correspond to routines used in views
  by setting Sroutine_hash_entry::belong_to_view member to point to TABLE_LIST
  object for topmost view which uses routine. We propagate this mark to all
  routines which are used by this routine and which we add to this set. We also
  mark tables used by such routine which we add to the list of tables for
  prelocking as belonging to this view.
sql/sp.h:
  sp_cache_routines_and_add_tables_for_view()/for_triggers():
    To be able to determine correctly uppermost view which uses this view/table
    with trigger we have to pass pointer to TABLE_LIST object instead of pointer
    to view's LEX or to Table_triggers_list object.
sql/sp_head.cc:
  sp_head::add_used_tables_to_table_list():
    Added new argument which allows to mark tables which are added to table
    list for prelocking as belonging to view (this allows properly hide names
    of tables which are used in routines used by views).
sql/sp_head.h:
  sp_head::add_used_tables_to_table_list():
    Added new argument which allows to mark tables which are added to table
    list for prelocking as belonging to view (this allows properly hide names
    of tables which are used in routines used by views).
sql/sql_base.cc:
  open_tables():
    sp_cache_routines_and_add_tables_for_view()/for_triggers() now accept
    pointer to table list element as last argument, this allows them to determine
    correctly uppermost view which uses this view/table with trigger.
sql/sql_trigger.h:
  Table_triggers_list:
    sp_cache_routines_and_add_tables_for_triggers() now accept pointer to table
    list element as last argument, this allows to determine correctly uppermost
    view which uses this table with trigger.
parent e0367223
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -1055,3 +1055,43 @@ Db Name Type Definer Modified Created Security_type Comment
mysqltest2	p1	PROCEDURE	root@localhost	0000-00-00 00:00:00	0000-00-00 00:00:00	DEFINER	
drop database mysqltest2;
use test;
drop function if exists bug11555_1;
drop function if exists bug11555_2;
drop view if exists v1, v2, v3, v4;
create function bug11555_1() returns int return (select max(i) from t1);
create function bug11555_2() returns int return bug11555_1();
create view v1 as select bug11555_1();
ERROR 42S02: Table 'test.t1' doesn't exist
create view v2 as select bug11555_2();
ERROR 42S02: Table 'test.t1' doesn't exist
create table t1 (i int);
create view v1 as select bug11555_1();
create view v2 as select bug11555_2();
create view v3 as select * from v1;
drop table t1;
select * from v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v2;
ERROR HY000: View 'test.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v3;
ERROR HY000: View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
create view v4 as select * from v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1, v2, v3;
drop function bug11555_1;
drop function bug11555_2;
create table t1 (i int);
create table t2 (i int);
create trigger t1_ai after insert on t1 for each row insert into t2 values (new.i);
create view v1 as select * from t1;
drop table t2;
insert into v1 values (1);
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop trigger t1_ai;
create function bug11555_1() returns int return (select max(i) from t2);
create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1();
insert into v1 values (2);
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop function bug11555_1;
drop table t1;
drop view v1;
+3 −3
Original line number Diff line number Diff line
@@ -1933,11 +1933,11 @@ create function f1 () returns int return (select max(col1) from t1);
DROP TABLE t1;
CHECK TABLE v1, v2, v3, v4, v5, v6;
Table	Op	Msg_type	Msg_text
test.v1	check	error	Table 'test.t1' doesn't exist
test.v1	check	error	View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v2	check	status	OK
test.v3	check	error	Table 'test.t1' doesn't exist
test.v3	check	error	View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v4	check	status	OK
test.v5	check	error	Table 'test.t1' doesn't exist
test.v5	check	error	View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v6	check	status	OK
drop function f1;
drop function f2;
+61 −0
Original line number Diff line number Diff line
@@ -1519,6 +1519,67 @@ drop database mysqltest2;
use test;


# BUG#11555 "Stored procedures: current SP tables locking make 
# impossible view security". We should not expose names of tables
# which are implicitly used by view (via stored routines/triggers).
#
# Note that SQL standard assumes that you simply won't be able drop table
# and leave some objects (routines/views/triggers) which were depending on
# it. Such objects should be dropped in advance (by default) or will be
# dropped simultaneously with table (DROP TABLE with CASCADE clause).
# So these tests probably should go away once we will implement standard
# behavior.
--disable_warnings
drop function if exists bug11555_1;
drop function if exists bug11555_2;
drop view if exists v1, v2, v3, v4;
--enable_warnings
create function bug11555_1() returns int return (select max(i) from t1);
create function bug11555_2() returns int return bug11555_1();
# It is OK to report name of implicitly used table which is missing
# when we create view.
--error ER_NO_SUCH_TABLE
create view v1 as select bug11555_1();
--error ER_NO_SUCH_TABLE
create view v2 as select bug11555_2();
# But we should hide name of missing implicitly used table when we use view
create table t1 (i int);
create view v1 as select bug11555_1();
create view v2 as select bug11555_2();
create view v3 as select * from v1;
drop table t1;
--error ER_VIEW_INVALID
select * from v1;
--error ER_VIEW_INVALID
select * from v2;
--error ER_VIEW_INVALID
select * from v3;
# Note that creation of view which depends on broken view is yet 
# another form of view usage.
--error ER_VIEW_INVALID
create view v4 as select * from v1;
drop view v1, v2, v3;
# We also should hide details about broken triggers which are
# invoked for view.
drop function bug11555_1;
drop function bug11555_2;
create table t1 (i int);
create table t2 (i int);
create trigger t1_ai after insert on t1 for each row insert into t2 values (new.i);
create view v1 as select * from t1;
drop table t2;
--error ER_VIEW_INVALID
insert into v1 values (1);
drop trigger t1_ai;
create function bug11555_1() returns int return (select max(i) from t2);
create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1();
--error ER_VIEW_INVALID
insert into v1 values (2);
drop function bug11555_1;
drop table t1;
drop view v1;


# BUG#NNNN: New bug synopsis
#
#--disable_warnings
+0 −1
Original line number Diff line number Diff line
@@ -1744,7 +1744,6 @@ drop function f1;
CHECK TABLE v1, v2, v3, v4, v5, v6;
create function f1 () returns int return (select max(col1) from t1);
DROP TABLE t1;
# following will show underlying table until BUG#11555 fix
CHECK TABLE v1, v2, v3, v4, v5, v6;
drop function f1;
drop function f2;
+53 −30
Original line number Diff line number Diff line
@@ -1199,6 +1199,12 @@ struct Sroutine_hash_entry
    for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
  */
  Sroutine_hash_entry *next;
  /*
    Uppermost view which directly or indirectly uses this routine.
    0 if routine is not used in view. Note that it also can be 0 if
    statement uses routine both via view and directly.
  */
  TABLE_LIST *belong_to_view;
};


@@ -1253,9 +1259,11 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,

  SYNOPSIS
    add_used_routine()
      lex     - LEX representing statement
      arena   - arena in which memory for new element will be allocated
      key     - key for the hash representing set
      lex             LEX representing statement
      arena           Arena in which memory for new element will be allocated
      key             Key for the hash representing set
      belong_to_view  Uppermost view which uses this routine
                      (0 if routine is not used by view)

  NOTES
    Will also add element to end of 'LEX::sroutines_list' list.
@@ -1278,7 +1286,8 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
*/

static bool add_used_routine(LEX *lex, Query_arena *arena,
                             const LEX_STRING *key)
                             const LEX_STRING *key,
                             TABLE_LIST *belong_to_view)
{
  if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
  {
@@ -1292,6 +1301,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
    memcpy(rn->key.str, key->str, key->length);
    my_hash_insert(&lex->sroutines, (byte *)rn);
    lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
    rn->belong_to_view= belong_to_view;
    return TRUE;
  }
  return FALSE;
@@ -1322,7 +1332,7 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
                         sp_name *rt, char rt_type)
{
  rt->set_routine_type(rt_type);
  (void)add_used_routine(lex, arena, &rt->m_sroutines_key);
  (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0);
  lex->sroutines_list_own_last= lex->sroutines_list.next;
  lex->sroutines_list_own_elements= lex->sroutines_list.elements;
}
@@ -1392,20 +1402,23 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src)

  SYNOPSIS
    sp_update_stmt_used_routines()
      thd - thread context
      lex - LEX representing statement
      src - hash representing set from which routines will be added
      thd             Thread context
      lex             LEX representing statement
      src             Hash representing set from which routines will be added
      belong_to_view  Uppermost view which uses these routines, 0 if none

  NOTE
    It will also add elements to end of 'LEX::sroutines_list' list.
*/

static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
static void
sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
                             TABLE_LIST *belong_to_view)
{
  for (uint i=0 ; i < src->records ; i++)
  {
    Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
    (void)add_used_routine(lex, thd->stmt_arena, &rt->key);
    (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
  }
}

@@ -1419,16 +1432,18 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
      thd             Thread context
      lex             LEX representing statement
      src             List representing set from which routines will be added
      belong_to_view  Uppermost view which uses these routines, 0 if none

  NOTE
    It will also add elements to end of 'LEX::sroutines_list' list.
*/

static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src)
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src,
                                         TABLE_LIST *belong_to_view)
{
  for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
       rt; rt= rt->next)
    (void)add_used_routine(lex, thd->stmt_arena, &rt->key);
    (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
}


@@ -1493,8 +1508,11 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
    {
      if (!(first && first_no_prelock))
      {
        sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines);
        result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
        sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines,
                                     rt->belong_to_view);
        result|= sp->add_used_tables_to_table_list(thd,
                                                   &lex->query_tables_last,
                                                   rt->belong_to_view);
      }
    }
    first= FALSE;
@@ -1536,17 +1554,18 @@ sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock)

  SYNOPSIS
    sp_cache_routines_and_add_tables_for_view()
      thd     - thread context
      lex     - LEX representing statement
      aux_lex - LEX representing view
      thd   Thread context
      lex   LEX representing statement
      view  Table list element representing view
*/

void
sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view)
{
  Sroutine_hash_entry **last_cached_routine_ptr=
                          (Sroutine_hash_entry **)lex->sroutines_list.next;
  sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines_list);
  sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list,
                               view->top_table());
  (void)sp_cache_routines_and_add_tables_aux(thd, lex, 
                                             *last_cached_routine_ptr, FALSE);
}
@@ -1559,16 +1578,18 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)

  SYNOPSIS
    sp_cache_routines_and_add_tables_for_triggers()
      thd      - thread context
      lex      - LEX respresenting statement
      triggers - triggers of the table
      thd    thread context
      lex    LEX respresenting statement
      table  Table list element for table with trigger
*/

void
sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
                                              Table_triggers_list *triggers)
                                              TABLE_LIST *table)
{
  if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key))
  Table_triggers_list *triggers= table->table->triggers;
  if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key,
                       table->belong_to_view))
  {
    Sroutine_hash_entry **last_cached_routine_ptr=
                            (Sroutine_hash_entry **)lex->sroutines_list.next;
@@ -1578,10 +1599,12 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
      {
        if (triggers->bodies[i][j])
        {
          (void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
                                          &lex->query_tables_last);
          (void)triggers->bodies[i][j]->
                add_used_tables_to_table_list(thd, &lex->query_tables_last,
                                              table->belong_to_view);
          sp_update_stmt_used_routines(thd, lex,
                                       &triggers->bodies[i][j]->m_sroutines);
                                       &triggers->bodies[i][j]->m_sroutines,
                                       table->belong_to_view);
        }
      }
    }
Loading