8000 Further fixes to the pg_get_expr() security fix in back branches. · jandas/postgres@42c3871 · GitHub
[go: up one dir, main page]

Skip to content

Commit 42c3871

Browse files
committed
Further fixes to the pg_get_expr() security fix in back branches.
It now emerges that the JDBC driver expects to be able to use pg_get_expr() on an output of a sub-SELECT. So extend the check logic to be able to recurse into a sub-SELECT to see if the argument is ultimately coming from an appropriate column. Per report from Thomas Kellerer.
1 parent dbf859c commit 42c3871

File tree

1 file changed

+56
-24
lines changed

1 file changed

+56
-24
lines changed

src/backend/parser/parse_func.c

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "parser/parse_func.h"
3131
#include "parser/parse_relation.h"
3232
#include "parser/parse_type.h"
33+
#include "parser/parsetree.h"
3334
#include "utils/builtins.h"
3435
#include "utils/fmgroids.h"
3536
#include "utils/lsyscache.h"
@@ -44,6 +45,7 @@ static Oid **gen_cross_product(InhPaths *arginh, int nargs);
4445
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
4546
static void unknown_attribute(const char *schemaname, const char *relname,
4647
const char *attname);
48+
static bool check_pg_get_expr_arg(ParseState *pstate, Node *arg, int netlevelsup);
4749

4850

4951
/*
@@ -1584,9 +1586,7 @@ GetRTEByRangeTablePosn(ParseState *pstate,
15841586
void
15851587
check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args)
15861588
{
1587-
bool allowed = false;
15881589
Node *arg;
1589-
int netlevelsup;
15901590

15911591
/* if not being called for pg_get_expr, do nothing */
15921592
if (fnoid != F_PG_GET_EXPR && fnoid != F_PG_GET_EXPR_EXT)
@@ -1598,59 +1598,91 @@ check_pg_get_expr_args(ParseState *pstate, Oid fnoid, List *args)
15981598

15991599
/*
16001600
* The first argument must be a Var referencing one of the allowed
1601-
* system-catalog columns. It could be a join alias Var, though.
1601+
* system-catalog columns. It could be a join alias Var or subquery
1602+
* reference Var, though, so we need a recursive subroutine to chase
1603+
* through those possibilities.
16021604
*/
16031605
Assert(args != NIL);
16041606
arg = (Node *) lfirst(args);
1605-
netlevelsup = 0;
16061607

1607-
restart:
1608-
if (IsA(arg, Var))
1608+
if (!check_pg_get_expr_arg(pstate, arg, 0))
1609+
ereport(ERROR,
1610+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1611+
errmsg("argument to pg_get_expr() must come from system catalogs")));
1612+
}
1613+
1614+
static bool
1615+
check_pg_get_expr_arg(ParseState *pstate, Node *arg, int netlevelsup)
1616+
{
1617+
if (arg && IsA(arg, Var))
16091618
{
16101619
Var *var = (Var *) arg;
16111620
RangeTblEntry *rte;
1621+
AttrNumber attnum;
16121622

16131623
netlevelsup += var->varlevelsup;
16141624
rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
1625+
attnum = var->varattno;
16151626

16161627
if (rte->rtekind == RTE_JOIN)
16171628
{
1618-
/* Expand join alias reference */
1619-
if (var->varattno > 0 &&
1620-
var->varattno <= length(rte->joinaliasvars))
1629+
/* Recursively examine join alias variable */
1630+
if (attnum > 0 &&
1631+
attnum <= length(rte->joinaliasvars))
16211632
{
1622-
arg = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
1623-
goto restart;
1633+
arg = (Node *) nth(attnum - 1, rte->joinaliasvars);
1634+
return check_pg_get_expr_arg(pstate, arg, netlevelsup);
16241635
}
16251636
}
1637+
else if (rte->rtekind == RTE_SUBQUERY)
1638+
{
1639+
/* Subselect-in-FROM: examine sub-select's output expr */
1640+
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
1641+
attnum);
1642+
ParseState mypstate;
1643+
1644+
if (ste == NULL || ste->resdom->resjunk)
1645+
elog(ERROR, "subquery %s does not have attribute %d",
1646+
rte->eref->aliasname, attnum);
1647+
arg = (Node *) ste->expr;
1648+
1649+
/*
1650+
* Recurse into the sub-select to see what its expr refers to.
1651+
* We have to build an additional level of ParseState to keep in
1652+
* step with varlevelsup in the subselect.
1653+
*/
1654+
MemSet(&mypstate, 0, sizeof(mypstate));
1655+
mypstate.parentParseState = pstate;
1656+
mypstate.p_rtable = rte->subquery->rtable;
1657+
/* don't bother filling the rest of the fake pstate */
1658+
1659+
return check_pg_get_expr_arg(&mypstate, arg, 0);
1660+
}
16261661
else if (rte->rtekind == RTE_RELATION)
16271662
{
16281663
if (rte->relid == get_system_catalog_relid(IndexRelationName))
16291664
{
1630-
if (var->varattno == Anum_pg_index_indexprs ||
1631-
var->varattno == Anum_pg_index_indpred)
1632-
allowed = true;
1665+
if (attnum == Anum_pg_index_indexprs ||
1666+
attnum == Anum_pg_index_indpred)
1667+
return true;
16331668
}
16341669
else if (rte->relid == get_system_catalog_relid(AttrDefaultRelationName))
16351670
{
1636-
if (var->varattno == Anum_pg_attrdef_adbin)
1637-
allowed = true;
1671+
if (attnum == Anum_pg_attrdef_adbin)
1672+
return true;
16381673
}
16391674
else if (rte->relid == get_system_catalog_relid(ConstraintRelationName))
16401675
{
1641-
if (var->varattno == Anum_pg_constraint_conbin)
1642-
allowed = true;
1676+
if (attnum == Anum_pg_constraint_conbin)
1677+
return true;
16431678
}
16441679
else if (rte->relid == get_system_catalog_relid(TypeRelationName))
16451680
{
1646-
if (var->varattno == Anum_pg_type_typdefaultbin)
1647-
allowed = true;
1681+
if (attnum == Anum_pg_type_typdefaultbin)
1682+
return true;
16481683
}
16491684
}
16501685
}
16511686

1652-
if (!allowed)
1653-
ereport(ERROR,
1654-
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1655-
errmsg("argument to pg_get_expr() must come from system catalogs")));
1687+
return false;
16561688
}

0 commit comments

Comments
 (0)
0