aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_func.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_func.c')
-rw-r--r--src/backend/parser/parse_func.c113
1 files changed, 112 insertions, 1 deletions
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 5de08dc78c8..8e0f42fc427 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,15 +8,18 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.224 2010/05/30 18:10:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.225 2010/07/29 23:16:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "catalog/pg_attrdef.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
+#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_agg.h"
@@ -26,6 +29,7 @@
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -494,6 +498,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
retval = (Node *) wfunc;
}
+ /* Hack to protect pg_get_expr() against misuse */
+ check_pg_get_expr_args(pstate, funcid, fargs);
+
return retval;
}
@@ -1580,3 +1587,107 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
return oid;
}
+
+
+/*
+ * pg_get_expr() is a system function that exposes the expression
+ * deparsing functionality in ruleutils.c to users. Very handy, but it was
+ * later realized that the functions in ruleutils.c don't check the input
+ * rigorously, assuming it to come from system catalogs and to therefore
+ * be valid. That makes it easy for a user to crash the backend by passing
+ * a maliciously crafted string representation of an expression to
+ * pg_get_expr().
+ *
+ * There's a lot of code in ruleutils.c, so it's not feasible to add
+ * water-proof input checking after the fact. Even if we did it once, it
+ * would need to be taken into account in any future patches too.
+ *
+ * Instead, we restrict pg_rule_expr() to only allow input from system
+ * catalogs. This is a hack, but it's the most robust and easiest
+ * to backpatch way of plugging the vulnerability.
+ *
+ * This is transparent to the typical usage pattern of
+ * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo',
+ * ...)", even if 'foo' is a valid expression fetched earlier from a
+ * system catalog. Hopefully there aren't many clients doing that out there.
+ */
+void
+check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args)
+{
+ bool allowed = false;
+ Node *arg;
+ int netlevelsup;
+
+ /* if not being called for pg_get_expr, do nothing */
+ if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT)
+ return;
+
+ /* superusers are allowed to call it anyway (dubious) */
+ if (superuser())
+ return;
+
+ /*
+ * The first argument must be a Var referencing one of the allowed
+ * system-catalog columns. It could be a join alias Var, though.
+ */
+ Assert(list_length(args) > 1);
+ arg = (Node *) linitial(args);
+ netlevelsup = 0;
+
+restart:
+ if (IsA(arg, Var))
+ {
+ Var *var = (Var *) arg;
+ RangeTblEntry *rte;
+
+ netlevelsup += var->varlevelsup;
+ rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
+
+ if (rte->rtekind == RTE_JOIN)
+ {
+ /* Expand join alias reference */
+ if (var->varattno > 0 &&
+ var->varattno <= list_length(rte->joinaliasvars))
+ {
+ arg = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1);
+ goto restart;
+ }
+ }
+ else if (rte->rtekind == RTE_RELATION)
+ {
+ switch (rte->relid)
+ {
+ case IndexRelationId:
+ if (var->varattno == Anum_pg_index_indexprs ||
+ var->varattno == Anum_pg_index_indpred)
+ allowed = true;
+ break;
+
+ case AttrDefaultRelationId:
+ if (var->varattno == Anum_pg_attrdef_adbin)
+ allowed = true;
+ break;
+
+ case ProcedureRelationId:
+ if (var->varattno == Anum_pg_proc_proargdefaults)
+ allowed = true;
+ break;
+
+ case ConstraintRelationId:
+ if (var->varattno == Anum_pg_constraint_conbin)
+ allowed = true;
+ break;
+
+ case TypeRelationId:
+ if (var->varattno == Anum_pg_type_typdefaultbin)
+ allowed = true;
+ break;
+ }
+ }
+ }
+
+ if (!allowed)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("argument to pg_get_expr() must come from system catalogs")));
+}