Re: [v9.2] Fix leaky-view problem, part 1

From: Kohei KaiGai <kaigai(at)kaigai(dot)gr(dot)jp>
To: Noah Misch <noah(at)2ndquadrant(dot)com>
Cc: Robert Haas <robertmhaas(at)gmail(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Kohei Kaigai <Kohei(dot)Kaigai(at)emea(dot)nec(dot)com>, Stephen Frost <sfrost(at)snowman(dot)net>, Heikki Linnakangas <heikki(dot)linnakangas(at)enterprisedb(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: [v9.2] Fix leaky-view problem, part 1
Date: 2011-07-06 20:25:12
Message-ID: CADyhKSU-dLudYu53FWaJFRYkeazn0_vFKFdgqUVra=_pvqy4Vw@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Thanks for your detailed viewing and testing.

The attached patches are revised version.

Part-0)
* The patch was re-generated using context diff, instead of unified diff
* Documentation on ALTER VIEW was added
* Behavior of CREATE OR REPLACE VIEW was revised; when we replace
an existing view, reloption shall be reset, even if a particular
value was set.
* And, cosmetic changes; eliminate warnings due to lack of type cast.

Part-1)
* I removed the code to increment depth by 2, and preserve the latest bit,
because we have no module to utilize this mechanism right now.

Thanks,

2011/7/5 Noah Misch <noah(at)2ndquadrant(dot)com>:
> On Sun, Jul 03, 2011 at 11:33:38AM +0200, Kohei KaiGai wrote:
>> The attached patches are revised version.
>>
>> The part-0 provides 'security_barrier' option for view definition, and performs
>> as a common basis of part-1 and part-2 patches.
>> Syntax is extended as follows:
>>
>>   CREATE VIEW view_name [WITH (param [=value])] AS query;
>>
>> We can also turn on/off this security_barrier setting by ALTER TABLE with
>> SET/RESET options.
>>
>> The part-1 patch enforces the qualifiers originally located under the security
>> barrier view to be launched prior to ones supplied on upper level.
>> The differences from the previous version is this barrier become conditional,
>> not always. So, existing optimization will be applied without any changes
>> onto non-security-barrier views.
>
> I tested various query trees I considered interesting, and this version had
> sound semantics for all of them.  I have one suggestion for CREATE OR REPLACE
> VIEW semantics, plus various cosmetic comments.
>
> These patches are unified diffs, rather than project-standard context diffs.
>
> Part 0:
>> --- a/doc/src/sgml/ref/create_view.sgml
>> +++ b/doc/src/sgml/ref/create_view.sgml
>> @@ -22,6 +22,7 @@ PostgreSQL documentation
>>   <refsynopsisdiv>
>>  <synopsis>
>>  CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
>> +    [ WITH ( parameter [= value] [, ... ] ) ]
>
> This needs a bit more markup; see the corresponding case in create_table.sgml.
>
> alter_view.sgml also needs an update.  Incidentally, we should use ALTER VIEW
> SET OPTION when referring to setting this for a view.  ALTER TABLE SET OPTION
> will also support views, since that's the general pattern for tablecmds.c type
> checks, but that's largely an implementation detail.
>
>> --- a/src/backend/commands/view.c
>> +++ b/src/backend/commands/view.c
>> @@ -97,7 +97,8 @@ isViewOnTempTable_walker(Node *node, void *context)
>>   *---------------------------------------------------------------------
>>   */
>>  static Oid
>> -DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>> +DefineVirtualRelation(const RangeVar *relation, List *tlist,
>> +                                       bool replace, List *options)
>>  {
>>       Oid                     viewOid,
>>                               namespaceId;
>
> This hunk and the hunk for the function's caller get rejects due to another
> recent signature change.
>
>> @@ -167,6 +168,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>>       {
>>               Relation        rel;
>>               TupleDesc       descriptor;
>> +             List       *atcmds = NIL;
>> +             AlterTableCmd *atcmd;
>>
>>               /*
>>                * Yes.  Get exclusive lock on the existing view ...
>> @@ -211,14 +214,11 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>>                */
>>               if (list_length(attrList) > rel->rd_att->natts)
>>               {
>> -                     List       *atcmds = NIL;
>>                       ListCell   *c;
>>                       int                     skip = rel->rd_att->natts;
>>
>>                       foreach(c, attrList)
>>                       {
>> -                             AlterTableCmd *atcmd;
>> -
>>                               if (skip > 0)
>>                               {
>>                                       skip--;
>> @@ -229,10 +229,24 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>>                               atcmd->def = (Node *) lfirst(c);
>>                               atcmds = lappend(atcmds, atcmd);
>>                       }
>> -                     AlterTableInternal(viewOid, atcmds, true);
>>               }
>>
>>               /*
>> +              * If optional parameters are specified, we must set options
>> +              * using ALTER TABLE SET OPTION internally.
>
> I think CREATE OR REPLACE VIEW should replace the option list, while ALTER
> VIEW SET OPTION should retain its current behavior.  That is, this should
> leave the view with no options set:
>
> create or replace view v0(n) with (security_barrier) as values (1), (2), (3), (4);
> select reloptions from pg_class where oid = 'v0'::regclass;
> create or replace view v0(n) as values (4), (3), (2), (1);
> select reloptions from pg_class where oid = 'v0'::regclass;
>
>> +              */
>> +             if (list_length(options) > 0)
>> +             {
>> +                     atcmd = makeNode(AlterTableCmd);
>> +                     atcmd->subtype = AT_SetRelOptions;
>> +                     atcmd->def = options;
>
> This line produces a warning:
>
> view.c: In function `DefineVirtualRelation':
> view.c:240: warning: assignment from incompatible pointer type
>
>> +
>> +                     atcmds = lappend(atcmds, atcmd);
>> +             }
>> +             if (atcmds != NIL)
>> +                     AlterTableInternal(viewOid, atcmds, true);
>> +
>> +             /*
>>                * Seems okay, so return the OID of the pre-existing view.
>>                */
>>               relation_close(rel, NoLock);    /* keep the lock! */
>
> Part 1:
>> --- a/src/backend/optimizer/plan/planner.c
>> +++ b/src/backend/optimizer/plan/planner.c
>
>> @@ -2993,6 +3001,131 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
>>       }
>>  }
>>
>> +/*
>> + * mark_qualifiers_depth
>> + *
>> + * It marks depth field of the each expression nodes that eventually
>> + * invokes functions, to track the original nest-level. On the evaluation
>> + * of qualifiers within WHERE or JOIN ... ON clauses during relation scans,
>> + * these items shall be reordered according to the nest-level and estimated
>> + * cost.
>> + * The optimizer may pull-up simple sub-queries or join clause, and
>> + * qualifiers to filter out tuples shall be mixed with ones in upper-
>> + * level. Thus, we need to track the original nest-level of qualifiers
>> + * to prevent reverse of order in evaluation, because some of qualifiers
>> + * can have side-effects that allows to leak supplied argument to outside.
>> + * It can be abused to break row-level security using a user defined function
>> + * with very small estimated cost, so nest level of qualifiers originated
>> + * from is used as a criteria, rather than estimated cost, to decide order
>> + * to evaluate qualifiers.
>> + */
>> +static bool
>> +mark_qualifiers_depth_walker(Node *node, void *context)
>> +{
>> +     int             depth = *((int *)(context));
>> +
>> +     if (node == NULL)
>> +             return false;
>> +     if (IsA(node, FuncExpr))
>> +     {
>> +             FuncExpr   *exp = (FuncExpr *)node;
>> +
>> +             exp->depth = depth | (exp->depth & 1);
>
> Why did these change from plain "exp->depth = depth;" of the last version?
> Since no core code sets a 1-bit in a depth value, I assume it must be related
> to your future-use design for that bit.  If so: could an external module
> realistically take advantage of this?  If yes, then a mere comment is in
> order.  If not, I think we should remove this (and the incrementing by 2) and
> add it again in the future patch that makes use thereof.
>
>> --- a/src/test/regress/sql/select_views.sql
>> +++ b/src/test/regress/sql/select_views.sql
>> @@ -8,3 +8,42 @@ SELECT * FROM street;
>>  SELECT name, #thepath FROM iexit ORDER BY 1, 2;
>>
>>  SELECT * FROM toyemp WHERE name = 'sharon';
>> +
>> +--
>> +-- Test for leaky-view
>> +--
>> +
>> +CREATE USER alice;
>> +CREATE FUNCTION f_leak(text, text)
>> +        RETURNS bool LANGUAGE 'plpgsql'
>> +        COST 0.00000001
>> +        AS 'begin raise notice ''% => %'', $1, $2; return true; end';
>> +CREATE TABLE credit_cards (
>> +    name   text,
>> +    number text,
>> +    expired    text
>> +);
>> +INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
>> +                                ('bob',   '5555-6666-7777-8888', 'Nov-2016'),
>> +                                ('eve',   '9801-2345-6789-0123', 'Jan-2018');
>> +CREATE VIEW your_credit_normal AS
>> +    SELECT * FROM credit_cards WHERE name = getpgusername();
>> +CREATE VIEW your_credit_secure WITH (security_barrier) AS
>> +    SELECT * FROM credit_cards WHERE name = getpgusername();
>> +
>> +GRANT SELECT ON your_credit_normal TO public;
>> +GRANT SELECT ON your_credit_secure TO public;
>> +-- run leaky view
>> +SET SESSION AUTHORIZATION alice;
>> +
>> +SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
>> +EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
>> +
>> +SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
>> +EXPLAIN (COSTS OFF) SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
>> +
>> +\c -
>> +-- cleanups
>> +DROP ROLE IF EXISTS alice;
>> +DROP FUNCTION IF EXISTS f_leak(text);
>> +DROP TABLE IF EXISTS credit_cards CASCADE;
>
> Keep the view around.  That way, pg_dump tests of the regression database will
> test the dumping of this view option.  (Your pg_dump support for this feature
> does work fine, though.)
>
> Thanks,
> nm
>

--
KaiGai Kohei <kaigai(at)kaigai(dot)gr(dot)jp>

Attachment Content-Type Size
pgsql-v9.2-fix-leaky-view-part-0.v4.patch application/octet-stream 21.6 KB
pgsql-v9.2-fix-leaky-view-part-1.v4.patch application/octet-stream 33.8 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message HarmeekSingh Bedi 2011-07-06 20:29:03 Expression Pruning in postgress
Previous Message Robert Haas 2011-07-06 19:17:44 Re: reducing the overhead of frequent table locks, v4