aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_aggregate.c1
-rw-r--r--src/backend/catalog/pg_proc.c2
-rw-r--r--src/backend/commands/functioncmds.c40
-rw-r--r--src/backend/commands/proclang.c3
-rw-r--r--src/backend/commands/typecmds.c1
-rw-r--r--src/backend/optimizer/path/allpaths.c11
-rw-r--r--src/backend/optimizer/util/clauses.c140
-rw-r--r--src/backend/parser/gram.y7
-rw-r--r--src/backend/utils/cache/lsyscache.c19
9 files changed, 212 insertions, 12 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 1c4d6460144..9ff70a52e6c 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -241,6 +241,7 @@ AggregateCreate(const char *aggName,
false, /* isWindowFunc */
false, /* security invoker (currently not
* definable for agg) */
+ false, /* isLeakProof */
false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 8c0d5f9e6c4..91ead4cb9d2 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -76,6 +76,7 @@ ProcedureCreate(const char *procedureName,
bool isAgg,
bool isWindowFunc,
bool security_definer,
+ bool isLeakProof,
bool isStrict,
char volatility,
oidvector *parameterTypes,
@@ -334,6 +335,7 @@ ProcedureCreate(const char *procedureName,
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
+ values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c692a658340..ce866a20a99 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -446,6 +446,7 @@ compute_common_attribute(DefElem *defel,
DefElem **volatility_item,
DefElem **strict_item,
DefElem **security_item,
+ DefElem **leakproof_item,
List **set_items,
DefElem **cost_item,
DefElem **rows_item)
@@ -471,6 +472,13 @@ compute_common_attribute(DefElem *defel,
*security_item = defel;
}
+ else if (strcmp(defel->defname, "leakproof") == 0)
+ {
+ if (*leakproof_item)
+ goto duplicate_error;
+
+ *leakproof_item = defel;
+ }
else if (strcmp(defel->defname, "set") == 0)
{
*set_items = lappend(*set_items, defel->arg);
@@ -564,6 +572,7 @@ compute_attributes_sql_style(List *options,
char *volatility_p,
bool *strict_p,
bool *security_definer,
+ bool *leakproof_p,
ArrayType **proconfig,
float4 *procost,
float4 *prorows)
@@ -575,6 +584,7 @@ compute_attributes_sql_style(List *options,
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_item = NULL;
+ DefElem *leakproof_item = NULL;
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
@@ -611,6 +621,7 @@ compute_attributes_sql_style(List *options,
&volatility_item,
&strict_item,
&security_item,
+ &leakproof_item,
&set_items,
&cost_item,
&rows_item))
@@ -653,6 +664,8 @@ compute_attributes_sql_style(List *options,
*strict_p = intVal(strict_item->arg);
if (security_item)
*security_definer = intVal(security_item->arg);
+ if (leakproof_item)
+ *leakproof_p = intVal(leakproof_item->arg);
if (set_items)
*proconfig = update_proconfig_value(NULL, set_items);
if (cost_item)
@@ -805,7 +818,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
Oid requiredResultType;
bool isWindowFunc,
isStrict,
- security;
+ security,
+ isLeakProof;
char volatility;
ArrayType *proconfig;
float4 procost;
@@ -828,6 +842,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
isWindowFunc = false;
isStrict = false;
security = false;
+ isLeakProof = false;
volatility = PROVOLATILE_VOLATILE;
proconfig = NULL;
procost = -1; /* indicates not set */
@@ -837,7 +852,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
compute_attributes_sql_style(stmt->options,
&as_clause, &language,
&isWindowFunc, &volatility,
- &isStrict, &security,
+ &isStrict, &security, &isLeakProof,
&proconfig, &procost, &prorows);
/* Look up the language and validate permissions */
@@ -875,6 +890,16 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
ReleaseSysCache(languageTuple);
/*
+ * Only superuser is allowed to create leakproof functions because
+ * it possibly allows unprivileged users to reference invisible tuples
+ * to be filtered out using views for row-level security.
+ */
+ if (isLeakProof && !superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("only superuser can define a leakproof function")));
+
+ /*
* Convert remaining parameters of CREATE to form wanted by
* ProcedureCreate.
*/
@@ -960,6 +985,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
false, /* not an aggregate */
isWindowFunc,
security,
+ isLeakProof,
isStrict,
volatility,
parameterTypes,
@@ -1238,6 +1264,7 @@ AlterFunction(AlterFunctionStmt *stmt)
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_def_item = NULL;
+ DefElem *leakproof_item = NULL;
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
@@ -1274,6 +1301,7 @@ AlterFunction(AlterFunctionStmt *stmt)
&volatility_item,
&strict_item,
&security_def_item,
+ &leakproof_item,
&set_items,
&cost_item,
&rows_item) == false)
@@ -1286,6 +1314,14 @@ AlterFunction(AlterFunctionStmt *stmt)
procForm->proisstrict = intVal(strict_item->arg);
if (security_def_item)
procForm->prosecdef = intVal(security_def_item->arg);
+ if (leakproof_item)
+ {
+ if (intVal(leakproof_item->arg) && !superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("only superuser can define a leakproof function")));
+ procForm->proleakproof = intVal(leakproof_item->arg);
+ }
if (cost_item)
{
procForm->procost = defGetNumeric(cost_item);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index a3a111d41a0..8d6a0416d5f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -131,6 +131,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
false, /* isAgg */
false, /* isWindowFunc */
false, /* security_definer */
+ false, /* isLeakProof */
false, /* isStrict */
PROVOLATILE_VOLATILE,
buildoidvector(funcargtypes, 0),
@@ -166,6 +167,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
false, /* isAgg */
false, /* isWindowFunc */
false, /* security_definer */
+ false, /* isLeakProof */
true, /* isStrict */
PROVOLATILE_VOLATILE,
buildoidvector(funcargtypes, 1),
@@ -204,6 +206,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
false, /* isAgg */
false, /* isWindowFunc */
false, /* security_definer */
+ false, /* isLeakProof */
true, /* isStrict */
PROVOLATILE_VOLATILE,
buildoidvector(funcargtypes, 1),
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c6bc6c8e876..3523554cfbd 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1521,6 +1521,7 @@ makeRangeConstructors(const char *name, Oid namespace,
false, /* isAgg */
false, /* isWindowFunc */
false, /* security_definer */
+ false, /* leakproof */
false, /* isStrict */
PROVOLATILE_IMMUTABLE, /* volatility */
constructorArgTypesVector, /* parameterTypes */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 19bf7340ad4..e99e4cc1761 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1042,16 +1042,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *clause = (Node *) rinfo->clause;
- /*
- * XXX. You might wonder why we're testing rte->security_barrier
- * qual-by-qual here rather than hoisting the test up into the
- * surrounding if statement; after all, the answer will be the
- * same for all quals. The answer is that we expect to shortly
- * change this logic to allow pushing down some quals that use only
- * "leakproof" operators even through a security barrier.
- */
if (!rinfo->pseudoconstant &&
- !rte->security_barrier &&
+ (!rte->security_barrier ||
+ !contain_leaky_functions(clause)) &&
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
{
/* Push it down */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 126d49452e2..cd3da46bc5e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -93,6 +93,7 @@ static bool contain_subplans_walker(Node *node, void *context);
static bool contain_mutable_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_walker(Node *node, void *context);
static bool contain_nonstrict_functions_walker(Node *node, void *context);
+static bool contain_leaky_functions_walker(Node *node, void *context);
static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
static List *find_nonnullable_vars_walker(Node *node, bool top_level);
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
@@ -1129,6 +1130,145 @@ contain_nonstrict_functions_walker(Node *node, void *context)
context);
}
+/*****************************************************************************
+ * Check clauses for non-leakproof functions
+ *****************************************************************************/
+
+/*
+ * contain_leaky_functions
+ * Recursively search for leaky functions within a clause.
+ *
+ * Returns true if any function call with side-effect may be present in the
+ * clause. Qualifiers from outside the a security_barrier view should not
+ * be pushed down into the view, lest the contents of tuples intended to be
+ * filtered out be revealed via side effects.
+ */
+bool
+contain_leaky_functions(Node *clause)
+{
+ return contain_leaky_functions_walker(clause, NULL);
+}
+
+static bool
+contain_leaky_functions_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+
+ switch (nodeTag(node))
+ {
+ case T_Var:
+ case T_Const:
+ case T_Param:
+ case T_ArrayExpr:
+ case T_NamedArgExpr:
+ case T_BoolExpr:
+ case T_RelabelType:
+ case T_CaseExpr:
+ case T_CaseTestExpr:
+ case T_RowExpr:
+ case T_MinMaxExpr:
+ case T_NullTest:
+ case T_BooleanTest:
+ case T_List:
+ /*
+ * We know these node types don't contain function calls; but
+ * something further down in the node tree might.
+ */
+ break;
+
+ case T_FuncExpr:
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (!get_func_leakproof(expr->funcid))
+ return true;
+ }
+ break;
+
+ case T_OpExpr:
+ case T_DistinctExpr: /* struct-equivalent to OpExpr */
+ case T_NullIfExpr: /* struct-equivalent to OpExpr */
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ set_opfuncid(expr);
+ if (!get_func_leakproof(expr->opfuncid))
+ return true;
+ }
+ break;
+
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ set_sa_opfuncid(expr);
+ if (!get_func_leakproof(expr->opfuncid))
+ return true;
+ }
+ break;
+
+ case T_CoerceViaIO:
+ {
+ CoerceViaIO *expr = (CoerceViaIO *) node;
+ Oid funcid;
+ Oid ioparam;
+ bool varlena;
+
+ getTypeInputInfo(exprType((Node *)expr->arg),
+ &funcid, &ioparam);
+ if (!get_func_leakproof(funcid))
+ return true;
+
+ getTypeOutputInfo(expr->resulttype, &funcid, &varlena);
+ if (!get_func_leakproof(funcid))
+ return true;
+ }
+ break;
+
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
+ Oid funcid;
+ Oid ioparam;
+ bool varlena;
+
+ getTypeInputInfo(exprType((Node *)expr->arg),
+ &funcid, &ioparam);
+ if (!get_func_leakproof(funcid))
+ return true;
+ getTypeOutputInfo(expr->resulttype, &funcid, &varlena);
+ if (!get_func_leakproof(funcid))
+ return true;
+ }
+ break;
+
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *opid;
+
+ foreach(opid, rcexpr->opnos)
+ {
+ Oid funcid = get_opcode(lfirst_oid(opid));
+
+ if (!get_func_leakproof(funcid))
+ return true;
+ }
+ }
+ break;
+
+ default:
+ /*
+ * If we don't recognize the node tag, assume it might be leaky.
+ * This prevents an unexpected security hole if someone adds a new
+ * node type that can call a function.
+ */
+ return true;
+ }
+ return expression_tree_walker(node, contain_leaky_functions_walker,
+ context);
+}
/*
* find_nonnullable_rels
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ac4b4635b94..d79576bcaa3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -526,7 +526,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
KEY
- LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P
@@ -6115,6 +6115,10 @@ common_func_opt_item:
{
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
}
+ | LEAKPROOF
+ {
+ $$ = makeDefElem("leakproof", (Node *)makeInteger(TRUE));
+ }
| COST NumericOnly
{
$$ = makeDefElem("cost", (Node *)$2);
@@ -12219,6 +12223,7 @@ unreserved_keyword:
| LAST_P
| LC_COLLATE_P
| LC_CTYPE_P
+ | LEAKPROOF
| LEVEL
| LISTEN
| LOAD
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index dccf380cc27..44dab822648 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1548,6 +1548,25 @@ func_volatile(Oid funcid)
}
/*
+ * get_func_leakproof
+ * Given procedure id, return the function's leakproof field.
+ */
+bool
+get_func_leakproof(Oid funcid)
+{
+ HeapTuple tp;
+ bool result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->proleakproof;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
* get_func_cost
* Given procedure id, return the function's procost field.
*/