src/backend/optimizer/util/plancat.c | 88 ++++++++++++++++++++++++++++++++++ 1 files changed, 88 insertions(+), 0 deletions(-) diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index de629e9..1d87394 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -25,8 +25,10 @@ #include "catalog/heap.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/pathnode.h" #include "optimizer/plancat.h" #include "optimizer/predtest.h" #include "optimizer/prep.h" @@ -998,6 +1000,76 @@ build_index_tlist(PlannerInfo *root, IndexOptInfo *index, } /* + * clause_touch_security_barrier + * + * It returns true, if the supplied clause touches attribute of relation + * with 'security_barrier' being set on its RangeTblEntry. + * The purpose of this check is to make sure row-level security performs + * correctly, even if user defined its own operator with function that + * have a side-effect to leak its arguments. + */ +static bool +clause_touch_security_barrier(Node *clause, PlannerInfo *root) +{ + if (!clause) + return false; + + if (IsA(clause, Var)) + { + Var *var = (Var *) clause; + RangeTblEntry *rte = root->simple_rte_array[var->varno]; + + if (rte->rtekind == RTE_RELATION) + { + /* + * XXX - Extension modules may append qualifiers on WHERE clause + * implicitly for row-level security, and set security_barrier of + * RangeTblEnty. In this case, we should not allow operator + * functions to reference statistics data that may originated from + * the tuples to be invisible. + */ + return rte->security_barrier; + } + else if (rte->rtekind == RTE_SUBQUERY) + { + Query *subqry = rte->subquery; + RelOptInfo *rel; + TargetEntry *ste; + + /* + * This clause touches an attribute of sub-query originated from + * a view with security_barrier option; that means view is defined + * to row-level security. Thus, user given operator functions + * should not reference statistics data that may originated from + * the tuples to be invisible. + */ + if (rte->security_barrier) + return true; + + if (subqry->setOperations || + subqry->groupClause || + subqry->distinctClause) + return false; + + rel = find_base_rel(root, var->varno); + Assert(rel->subroot && IsA(rel->subroot, PlannerInfo)); + + ste = get_tle_by_resno(subqry->targetList, var->varattno); + if (ste == NULL || ste->resjunk) + elog(ERROR, "subquery %s does not have attribute %d", + rte->eref->aliasname, var->varattno); + + return clause_touch_security_barrier((Node *)ste->expr, + rel->subroot); + } + return false; + } + return expression_tree_walker(clause, + clause_touch_security_barrier, + (void *) root); +} + +/* * restriction_selectivity * * Returns the selectivity of a specified restriction operator clause. @@ -1022,6 +1094,14 @@ restriction_selectivity(PlannerInfo *root, if (!oprrest) return (Selectivity) 0.5; + /* + * if the operator depends on values originated from security-barrier + * relation, we skip to call estimator function; to ensure row-level + * security. + */ + if (clause_touch_security_barrier((Node *) args, root)) + return (Selectivity) 0.5; + result = DatumGetFloat8(OidFunctionCall4(oprrest, PointerGetDatum(root), ObjectIdGetDatum(operatorid), @@ -1058,6 +1138,14 @@ join_selectivity(PlannerInfo *root, if (!oprjoin) return (Selectivity) 0.5; + /* + * if the operator depends on values originated from security-barrier + * relation, we skip to call estimator function; to ensure row-level + * security. + */ + if (clause_touch_security_barrier((Node *) args, root)) + return (Selectivity) 0.5; + result = DatumGetFloat8(OidFunctionCall5(oprjoin, PointerGetDatum(root), ObjectIdGetDatum(operatorid),