Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cd30728

Browse files
committed
Allow LEAKPROOF functions for better performance of security views.
We don't normally allow quals to be pushed down into a view created with the security_barrier option, but functions without side effects are an exception: they're OK. This allows much better performance in common cases, such as when using an equality operator (that might even be indexable). There is an outstanding issue here with the CREATE FUNCTION / ALTER FUNCTION syntax: there's no way to use ALTER FUNCTION to unset the leakproof flag. But I'm committing this as-is so that it doesn't have to be rebased again; we can fix up the grammar in a future commit. KaiGai Kohei, with some wordsmithing by me.
1 parent 2bbd88f commit cd30728

28 files changed

+3356
-2454
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,6 +4423,18 @@
44234423
function)</entry>
44244424
</row>
44254425

4426+
<row>
4427+
<entry><structfield>proleakproof</structfield></entry>
4428+
<entry><type>bool</type></entry>
4429+
<entry></entry>
4430+
<entry>
4431+
The function has no side effects. No information about the
4432+
arguments is conveyed except via the return value. Any function
4433+
that might throw an error depending on the values of its arguments
4434+
is not leakproof.
4435+
</entry>
4436+
</row>
4437+
44264438
<row>
44274439
<entry><structfield>proisstrict</structfield></entry>
44284440
<entry><type>bool</type></entry>

doc/src/sgml/ref/alter_function.sgml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
3333
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
3434

3535
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
36-
IMMUTABLE | STABLE | VOLATILE
36+
IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
3737
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
3838
COST <replaceable class="parameter">execution_cost</replaceable>
3939
ROWS <replaceable class="parameter">result_rows</replaceable>
@@ -191,6 +191,17 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
191191
</listitem>
192192
</varlistentry>
193193

194+
<varlistentry>
195+
<term><literal>LEAKPROOF</literal></term>
196+
<listitem>
197+
<para>
198+
Change whether the function is considered leakproof or not.
199+
See <xref linkend="sql-createfunction"> for more information about
200+
this capability.
201+
</para>
202+
</listitem>
203+
</varlistentry>
204+
194205
<varlistentry>
195206
<term><literal>COST</literal> <replaceable class="parameter">execution_cost</replaceable></term>
196207

doc/src/sgml/ref/create_function.sgml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ CREATE [ OR REPLACE ] FUNCTION
2626
| RETURNS TABLE ( <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">column_type</replaceable> [, ...] ) ]
2727
{ LANGUAGE <replaceable class="parameter">lang_name</replaceable>
2828
| WINDOW
29-
| IMMUTABLE | STABLE | VOLATILE
29+
| IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
3030
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
3131
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
3232
| COST <replaceable class="parameter">execution_cost</replaceable>
@@ -324,6 +324,23 @@ CREATE [ OR REPLACE ] FUNCTION
324324
</listitem>
325325
</varlistentry>
326326

327+
<varlistentry>
328+
<term><literal>LEAKPROOF</literal></term>
329+
<listitem>
330+
<para>
331+
<literal>LEAKPROOF</literal> indicates that the function has no side
332+
effects. It reveals no information about its arguments other than by
333+
its return value. For example, a function which throws an error message
334+
for some argument values but not others, or which includes the argument
335+
values in any error message, is not leakproof. The query planner may
336+
push leakproof functions (but not others) into views created with the
337+
<literal>security_barrier</literal> option. See
338+
<xref linkend="sql-createview"> and <xref linkend="rules-privileges">.
339+
This option can only be set by the superuser.
340+
</para>
341+
</listitem>
342+
</varlistentry>
343+
327344
<varlistentry>
328345
<term><literal>CALLED ON NULL INPUT</literal></term>
329346
<term><literal>RETURNS NULL ON NULL INPUT</literal></term>

doc/src/sgml/rules.sgml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,20 @@ CREATE VIEW phone_number WITH (security_barrier) AS
18901890
enabled by default.
18911891
</para>
18921892

1893+
<para>
1894+
The query planner has more flexibility when dealing with functions that
1895+
have no side effects. Such functions are referred to as LEAKPROOF, and
1896+
include many simple, commonly used operators, such as many equality
1897+
operators. The query planner can safely allow such functions to be evaluated
1898+
at any point in the query execution process, since invoking them on rows
1899+
invisible to the user will not leak any information about the unseen rows.
1900+
In contrast, a function that might throw an error depending on the values
1901+
received as arguments (such as one that throws an error in the event of
1902+
overflow or division by zero) are not leak-proof, and could provide
1903+
significant information about the unseen rows if applied before the security
1904+
view's row filters.
1905+
</para>
1906+
18931907
<para>
18941908
It is important to understand that even a view created with the
18951909
<literal>security_barrier</literal> option is intended to be secure only

src/backend/catalog/pg_aggregate.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ AggregateCreate(const char *aggName,
241241
false, /* isWindowFunc */
242242
false, /* security invoker (currently not
243243
* definable for agg) */
244+
false, /* isLeakProof */
244245
false, /* isStrict (not needed for agg) */
245246
PROVOLATILE_IMMUTABLE, /* volatility (not
246247
* needed for agg) */

src/backend/catalog/pg_proc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ ProcedureCreate(const char *procedureName,
7676
bool isAgg,
7777
bool isWindowFunc,
7878
bool security_definer,
79+
bool isLeakProof,
7980
bool isStrict,
8081
char volatility,
8182
oidvector *parameterTypes,
@@ -334,6 +335,7 @@ ProcedureCreate(const char *procedureName,
334335
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
335336
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
336337
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
338+
values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
337339
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
338340
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
339341
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);

src/backend/commands/functioncmds.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ compute_common_attribute(DefElem *defel,
446446
DefElem **volatility_item,
447447
DefElem **strict_item,
448448
DefElem **security_item,
449+
DefElem **leakproof_item,
449450
List **set_items,
450451
DefElem **cost_item,
451452
DefElem **rows_item)
@@ -471,6 +472,13 @@ compute_common_attribute(DefElem *defel,
471472

472473
*security_item = defel;
473474
}
475+
else if (strcmp(defel->defname, "leakproof") == 0)
476+
{
477+
if (*leakproof_item)
478+
goto duplicate_error;
479+
480+
*leakproof_item = defel;
481+
}
474482
else if (strcmp(defel->defname, "set") == 0)
475483
{
476484
*set_items = lappend(*set_items, defel->arg);
@@ -564,6 +572,7 @@ compute_attributes_sql_style(List *options,
564572
char *volatility_p,
565573
bool *strict_p,
566574
bool *security_definer,
575+
bool *leakproof_p,
567576
ArrayType **proconfig,
568577
float4 *procost,
569578
float4 *prorows)
@@ -575,6 +584,7 @@ compute_attributes_sql_style(List *options,
575584
DefElem *volatility_item = NULL;
576585
DefElem *strict_item = NULL;
577586
DefElem *security_item = NULL;
587+
DefElem *leakproof_item = NULL;
578588
List *set_items = NIL;
579589
DefElem *cost_item = NULL;
580590
DefElem *rows_item = NULL;
@@ -611,6 +621,7 @@ compute_attributes_sql_style(List *options,
611621
&volatility_item,
612622
&strict_item,
613623
&security_item,
624+
&leakproof_item,
614625
&set_items,
615626
&cost_item,
616627
&rows_item))
@@ -653,6 +664,8 @@ compute_attributes_sql_style(List *options,
653664
*strict_p = intVal(strict_item->arg);
654665
if (security_item)
655666
*security_definer = intVal(security_item->arg);
667+
if (leakproof_item)
668+
*leakproof_p = intVal(leakproof_item->arg);
656669
if (set_items)
657670
*proconfig = update_proconfig_value(NULL, set_items);
658671
if (cost_item)
@@ -805,7 +818,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
805818
Oid requiredResultType;
806819
bool isWindowFunc,
807820
isStrict,
808-
security;
821+
security,
822+
isLeakProof;
809823
char volatility;
810824
ArrayType *proconfig;
811825
float4 procost;
@@ -828,6 +842,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
828842
isWindowFunc = false;
829843
isStrict = false;
830844
security = false;
845+
isLeakProof = false;
831846
volatility = PROVOLATILE_VOLATILE;
832847
proconfig = NULL;
833848
procost = -1; /* indicates not set */
@@ -837,7 +852,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
837852
compute_attributes_sql_style(stmt->options,
838853
&as_clause, &language,
839854
&isWindowFunc, &volatility,
840-
&isStrict, &security,
855+
&isStrict, &security, &isLeakProof,
841856
&proconfig, &procost, &prorows);
842857

843858
/* Look up the language and validate permissions */
@@ -874,6 +889,16 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
874889

875890
ReleaseSysCache(languageTuple);
876891

892+
/*
893+
* Only superuser is allowed to create leakproof functions because
894+
* it possibly allows unprivileged users to reference invisible tuples
895+
* to be filtered out using views for row-level security.
896+
*/
897+
if (isLeakProof && !superuser())
898+
ereport(ERROR,
899+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
900+
errmsg("only superuser can define a leakproof function")));
901+
877902
/*
878903
* Convert remaining parameters of CREATE to form wanted by
879904
* ProcedureCreate.
@@ -960,6 +985,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
960985
false, /* not an aggregate */
961986
isWindowFunc,
962987
security,
988+
isLeakProof,
963989
isStrict,
964990
volatility,
965991
parameterTypes,
@@ -1238,6 +1264,7 @@ AlterFunction(AlterFunctionStmt *stmt)
12381264
DefElem *volatility_item = NULL;
12391265
DefElem *strict_item = NULL;
12401266
DefElem *security_def_item = NULL;
1267+
DefElem *leakproof_item = NULL;
12411268
List *set_items = NIL;
12421269
DefElem *cost_item = NULL;
12431270
DefElem *rows_item = NULL;
@@ -1274,6 +1301,7 @@ AlterFunction(AlterFunctionStmt *stmt)
12741301
&volatility_item,
12751302
&strict_item,
12761303
&security_def_item,
1304+
&leakproof_item,
12771305
&set_items,
12781306
&cost_item,
12791307
&rows_item) == false)
@@ -1286,6 +1314,14 @@ AlterFunction(AlterFunctionStmt *stmt)
12861314
procForm->proisstrict = intVal(strict_item->arg);
12871315
if (security_def_item)
12881316
procForm->prosecdef = intVal(security_def_item->arg);
1317+
if (leakproof_item)
1318+
{
1319+
if (intVal(leakproof_item->arg) && !superuser())
1320+
ereport(ERROR,
1321+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1322+
errmsg("only superuser can define a leakproof function")));
1323+
procForm->proleakproof = intVal(leakproof_item->arg);
1324+
}
12891325
if (cost_item)
12901326
{
12911327
procForm->procost = defGetNumeric(cost_item);

src/backend/commands/proclang.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
131131
false, /* isAgg */
132132
false, /* isWindowFunc */
133133
false, /* security_definer */
134+
false, /* isLeakProof */
134135
false, /* isStrict */
135136
PROVOLATILE_VOLATILE,
136137
buildoidvector(funcargtypes, 0),
@@ -166,6 +167,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
166167
false, /* isAgg */
167168
false, /* isWindowFunc */
168169
false, /* security_definer */
170+
false, /* isLeakProof */
169171
true, /* isStrict */
170172
PROVOLATILE_VOLATILE,
171173
buildoidvector(funcargtypes, 1),
@@ -204,6 +206,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
204206
false, /* isAgg */
205207
false, /* isWindowFunc */
206208
false, /* security_definer */
209+
false, /* isLeakProof */
207210
true, /* isStrict */
208211
PROVOLATILE_VOLATILE,
209212
buildoidvector(funcargtypes, 1),

src/backend/commands/typecmds.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,7 @@ makeRangeConstructors(const char *name, Oid namespace,
15211521
false, /* isAgg */
15221522
false, /* isWindowFunc */
15231523
false, /* security_definer */
1524+
false, /* leakproof */
15241525
false, /* isStrict */
15251526
PROVOLATILE_IMMUTABLE, /* volatility */
15261527
constructorArgTypesVector, /* parameterTypes */

src/backend/optimizer/path/allpaths.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,16 +1042,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
10421042
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
10431043
Node *clause = (Node *) rinfo->clause;
10441044

1045-
/*
1046-
* XXX. You might wonder why we're testing rte->security_barrier
1047-
* qual-by-qual here rather than hoisting the test up into the
1048-
* surrounding if statement; after all, the answer will be the
1049-
* same for all quals. The answer is that we expect to shortly
1050-
* change this logic to allow pushing down some quals that use only
1051-
* "leakproof" operators even through a security barrier.
1052-
*/
10531045
if (!rinfo->pseudoconstant &&
1054-
!rte->security_barrier &&
1046+
(!rte->security_barrier ||
1047+
!contain_leaky_functions(clause)) &&
10551048
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
10561049
{
10571050
/* Push it down */

0 commit comments

Comments
 (0)