Thread: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

[COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Joe Conway
Date:
Apply RLS policies to partitioned tables.

The new partitioned table capability added a new relkind, namely
RELKIND_PARTITIONED_TABLE. Update fireRIRrules() to apply RLS
policies on RELKIND_PARTITIONED_TABLE as it does RELKIND_RELATION.

In addition, add RLS regression test coverage for partitioned tables.

Issue raised by Fakhroutdinov Evgenievich and patch by Mike Palmiotto.
Regression test editorializing by me.

Discussion: https://postgr.es/m/flat/20170601065959.1486.69906@wrigleys.postgresql.org

Branch
------
master

Details
-------
https://git.postgresql.org/pg/commitdiff/4f7a95be2c112bdc8da5f7e46cbb743b8ba4cc21

Modified Files
--------------
src/backend/rewrite/rewriteHandler.c      |   3 +-
src/test/regress/expected/rowsecurity.out | 428 ++++++++++++++++++++++++++++++
src/test/regress/sql/rowsecurity.sql      | 142 ++++++++++
3 files changed, 572 insertions(+), 1 deletion(-)


Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Apply RLS policies to partitioned tables.

Buildfarm member skink has grown a "make check" failure with this commit.

==28150== VALGRINDERROR-BEGIN
==28150== Invalid read of size 8
==28150==    at 0x39A355: ExecInitModifyTable (nodeModifyTable.c:1862)
==28150==    by 0x37F0F8: ExecInitNode (execProcnode.c:168)
==28150==    by 0x37C219: InitPlan (execMain.c:1044)
==28150==    by 0x37C3AF: standard_ExecutorStart (execMain.c:256)
==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
==28150==    by 0x44FD9B: BackendStartup (postmaster.c:4003)
==28150==  Address 0xbbdbec8 is 8,008 bytes inside a recently re-allocated block of size 8,192 alloc'd
==28150==    at 0x4C2AB76: malloc (vg_replace_malloc.c:299)
==28150==    by 0x6002D2: AllocSetAlloc (aset.c:760)
==28150==    by 0x606EB5: MemoryContextAllocZeroAligned (mcxt.c:791)
==28150==    by 0x3832B1: CreateExecutorState (execUtils.c:99)
==28150==    by 0x37C2E6: standard_ExecutorStart (execMain.c:186)
==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
==28150==
==28150== VALGRINDERROR-END

The cited line is the ExecInitQual call here:

            /* varno = node->nominalRelation */
            mapped_wcoList = map_partition_varattnos(wcoList,
                                                     node->nominalRelation,
                                                     partrel, rel);
            foreach(ll, mapped_wcoList)
            {
                WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
                ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
                                                   mtstate->mt_plans[i]);

                wcoExprs = lappend(wcoExprs, wcoExpr);
            }

First guess is that map_partition_varattnos has forgotten to handle
WithCheckOption.qual.  If so, though, and if that's not resulting
in visible misbehavior in the regression tests, then we are missing
a case that the regression tests should be covering.

BTW, it might be advisable to use castNode(WithCheckOption, ...)
in the line before that.

            regards, tom lane


Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Joe Conway
Date:
On 06/11/2017 05:35 PM, Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>> Apply RLS policies to partitioned tables.
>
> Buildfarm member skink has grown a "make check" failure with this commit.
>
> ==28150== VALGRINDERROR-BEGIN
> ==28150== Invalid read of size 8
> ==28150==    at 0x39A355: ExecInitModifyTable (nodeModifyTable.c:1862)
> ==28150==    by 0x37F0F8: ExecInitNode (execProcnode.c:168)
> ==28150==    by 0x37C219: InitPlan (execMain.c:1044)
> ==28150==    by 0x37C3AF: standard_ExecutorStart (execMain.c:256)
> ==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
> ==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
> ==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
> ==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
> ==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
> ==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
> ==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
> ==28150==    by 0x44FD9B: BackendStartup (postmaster.c:4003)
> ==28150==  Address 0xbbdbec8 is 8,008 bytes inside a recently re-allocated block of size 8,192 alloc'd
> ==28150==    at 0x4C2AB76: malloc (vg_replace_malloc.c:299)
> ==28150==    by 0x6002D2: AllocSetAlloc (aset.c:760)
> ==28150==    by 0x606EB5: MemoryContextAllocZeroAligned (mcxt.c:791)
> ==28150==    by 0x3832B1: CreateExecutorState (execUtils.c:99)
> ==28150==    by 0x37C2E6: standard_ExecutorStart (execMain.c:186)
> ==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
> ==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
> ==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
> ==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
> ==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
> ==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
> ==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
> ==28150==
> ==28150== VALGRINDERROR-END
>
> The cited line is the ExecInitQual call here:
>
>             /* varno = node->nominalRelation */
>             mapped_wcoList = map_partition_varattnos(wcoList,
>                                                      node->nominalRelation,
>                                                      partrel, rel);
>             foreach(ll, mapped_wcoList)
>             {
>                 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
>                 ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
>                                                    mtstate->mt_plans[i]);
>
>                 wcoExprs = lappend(wcoExprs, wcoExpr);
>             }
>
> First guess is that map_partition_varattnos has forgotten to handle
> WithCheckOption.qual.  If so, though, and if that's not resulting
> in visible misbehavior in the regression tests, then we are missing
> a case that the regression tests should be covering.
>
> BTW, it might be advisable to use castNode(WithCheckOption, ...)
> in the line before that.

Drat. I'll take a look, but it would probably be good if someone
generally familiar with the partitioned tables patches have a look as well.

Joe


--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development


Attachment

Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Amit Langote
Date:
On 2017/06/12 11:09, Joe Conway wrote:
> On 06/11/2017 05:35 PM, Tom Lane wrote:
>> Joe Conway <mail@joeconway.com> writes:
>>> Apply RLS policies to partitioned tables.
>>
>> Buildfarm member skink has grown a "make check" failure with this commit.
>>
>> ==28150== VALGRINDERROR-BEGIN
>> ==28150== Invalid read of size 8
>> ==28150==    at 0x39A355: ExecInitModifyTable (nodeModifyTable.c:1862)
>> ==28150==    by 0x37F0F8: ExecInitNode (execProcnode.c:168)
>> ==28150==    by 0x37C219: InitPlan (execMain.c:1044)
>> ==28150==    by 0x37C3AF: standard_ExecutorStart (execMain.c:256)
>> ==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
>> ==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
>> ==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
>> ==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
>> ==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
>> ==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
>> ==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
>> ==28150==    by 0x44FD9B: BackendStartup (postmaster.c:4003)
>> ==28150==  Address 0xbbdbec8 is 8,008 bytes inside a recently re-allocated block of size 8,192 alloc'd
>> ==28150==    at 0x4C2AB76: malloc (vg_replace_malloc.c:299)
>> ==28150==    by 0x6002D2: AllocSetAlloc (aset.c:760)
>> ==28150==    by 0x606EB5: MemoryContextAllocZeroAligned (mcxt.c:791)
>> ==28150==    by 0x3832B1: CreateExecutorState (execUtils.c:99)
>> ==28150==    by 0x37C2E6: standard_ExecutorStart (execMain.c:186)
>> ==28150==    by 0x37C4B8: ExecutorStart (execMain.c:151)
>> ==28150==    by 0x4CCCCE: ProcessQuery (pquery.c:157)
>> ==28150==    by 0x4CCEDF: PortalRunMulti (pquery.c:1287)
>> ==28150==    by 0x4CDE19: PortalRun (pquery.c:800)
>> ==28150==    by 0x4C9E85: exec_simple_query (postgres.c:1099)
>> ==28150==    by 0x4CBF20: PostgresMain (postgres.c:4087)
>> ==28150==    by 0x44DC04: BackendRun (postmaster.c:4331)
>> ==28150==
>> ==28150== VALGRINDERROR-END
>>
>> The cited line is the ExecInitQual call here:
>>
>>             /* varno = node->nominalRelation */
>>             mapped_wcoList = map_partition_varattnos(wcoList,
>>                                                      node->nominalRelation,
>>                                                      partrel, rel);
>>             foreach(ll, mapped_wcoList)
>>             {
>>                 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
>>                 ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
>>                                                    mtstate->mt_plans[i]);
>>
>>                 wcoExprs = lappend(wcoExprs, wcoExpr);
>>             }
>>
>> First guess is that map_partition_varattnos has forgotten to handle
>> WithCheckOption.qual.  If so, though, and if that's not resulting
>> in visible misbehavior in the regression tests, then we are missing
>> a case that the regression tests should be covering.
>>
>> BTW, it might be advisable to use castNode(WithCheckOption, ...)
>> in the line before that.
>
> Drat. I'll take a look, but it would probably be good if someone
> generally familiar with the partitioned tables patches have a look as well.

I am looking too.  Commit 587cda35ca3 which added that code did add a test
in updatable_views.sql, but tests added by this commit have perhaps
exposed something not previously covered.

Thanks,
Amit



Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Tom Lane
Date:
Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> writes:
> On 2017/06/12 11:09, Joe Conway wrote:
>> On 06/11/2017 05:35 PM, Tom Lane wrote:
>>> First guess is that map_partition_varattnos has forgotten to handle
>>> WithCheckOption.qual.

>> Drat. I'll take a look, but it would probably be good if someone
>> generally familiar with the partitioned tables patches have a look as well.

> I am looking too.  Commit 587cda35ca3 which added that code did add a test
> in updatable_views.sql, but tests added by this commit have perhaps
> exposed something not previously covered.

My first guess above is wrong -- the undefined reference is actually
"mtstate->mt_plans[i]".  This is because mtstate->mt_num_partitions is
more than mtstate->mt_nplans in the failing case, so that the blithe
assumption that we can use the partition number to index into
mtstate->mt_plans is certainly wrong.  Don't know how this should
be corrected, offhand, but do we really have more than one plan
for a partitioned query?

The specific query that's failing is

(gdb) p debug_query_string
$1 = 0x11b6220 "INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1');"

and you can get a crash without all the valgrind effort by adding

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index bf26488..5013a6d 100644
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1844,1849 ****
--- 1844,1850 ----

        Assert(operation == CMD_INSERT);
        resultRelInfo = mtstate->mt_partitions;
+       Assert(mtstate->mt_num_partitions == mtstate->mt_nplans);
        wcoList = linitial(node->withCheckOptionLists);
        for (i = 0; i < mtstate->mt_num_partitions; i++)
        {

            regards, tom lane


Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Amit Langote
Date:
On 2017/06/12 11:51, Tom Lane wrote:
> Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> writes:
>> On 2017/06/12 11:09, Joe Conway wrote:
>>> On 06/11/2017 05:35 PM, Tom Lane wrote:
>>>> First guess is that map_partition_varattnos has forgotten to handle
>>>> WithCheckOption.qual.
>
>>> Drat. I'll take a look, but it would probably be good if someone
>>> generally familiar with the partitioned tables patches have a look as well.
>
>> I am looking too.  Commit 587cda35ca3 which added that code did add a test
>> in updatable_views.sql, but tests added by this commit have perhaps
>> exposed something not previously covered.
>
> My first guess above is wrong -- the undefined reference is actually
> "mtstate->mt_plans[i]".  This is because mtstate->mt_num_partitions is
> more than mtstate->mt_nplans in the failing case, so that the blithe
> assumption that we can use the partition number to index into
> mtstate->mt_plans is certainly wrong.

Yes, the code should never access mtstate->mt_plans[i] for i > 0, because
in this case (the INSERT case), there is only one plan.  Unlike
UPDATE/DELETE cases, we don't build plans for every partition in the
INSERT case.

> Don't know how this should
> be corrected, offhand, but do we really have more than one plan
> for a partitioned query?

There can't be, as mentioned, in the INSERT case.

How about something like the attached? The patch makes
mtstate->mt_plans[0] to be passed for all invocations of ExecInitQual() on
WithCheckOption.qual that is being initialized for each partition.  Note
that we are passing a PlanState that's based on the root parent table's
properties to initialize WITH CHECK OPTION quals to be checked against
rows that will be inserted into partitions (that is, after tuple-routing).

I tried to come up with a test that would cause the existing (unpatched)
code to crash, but couldn't.  How would one define a policy or WITH CHECK
OPTIONS view or something else, such that ExecInitQual() called on
WithCheckOptions.qual would try to access parent planstate and would crash
once parent becomes an invalid pointer (mtstate->mt_plans[i] for i > 0)?
After looking at the code downstream to ExecInitQual(), it seems that the
following cases would cases would cause parent planstate to be accessed:

* Qual references a whole row var
* Qual references an Aggref or a GroupingFunc or a WindowFunc
* Qual references a (Alternative) SubPlan

Thanks,
Amit

Attachment

Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Tom Lane
Date:
Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> writes:
> How about something like the attached? The patch makes
> mtstate->mt_plans[0] to be passed for all invocations of ExecInitQual() on
> WithCheckOption.qual that is being initialized for each partition.

Well, the question then is whether that behaves correctly.  Where it would
matter is if the WCO qual contains a SubPlan, because you'd be adding
multiple instances of the subplan to the PlanState's list.  I think it's
probably all right, but it might be wise to add a test case where there
is such a subplan.

Spacing and comment wording seem a bit random in this, too.

            regards, tom lane


Re: [COMMITTERS] pgsql: Apply RLS policies to partitioned tables.

From
Amit Langote
Date:
On 2017/06/12 22:46, Tom Lane wrote:
> Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> writes:
>> How about something like the attached? The patch makes
>> mtstate->mt_plans[0] to be passed for all invocations of ExecInitQual() on
>> WithCheckOption.qual that is being initialized for each partition.
>
> Well, the question then is whether that behaves correctly.  Where it would
> matter is if the WCO qual contains a SubPlan, because you'd be adding
> multiple instances of the subplan to the PlanState's list.  I think it's
> probably all right, but it might be wise to add a test case where there
> is such a subplan.
>
> Spacing and comment wording seem a bit random in this, too.

Was about to send the updated patch, but noticed that you already
committed it after adding the test.  Thanks a lot.

Regards,
Amit