Commit 5413eef8 authored by Tom Lane's avatar Tom Lane

Repair failure to check that a table is still compatible with a previously

made query plan.  Use of ALTER COLUMN TYPE creates a hazard for cached
query plans: they could contain Vars that claim a column has a different
type than it now has.  Fix this by checking during plan startup that Vars
at relation scan level match the current relation tuple descriptor.  Since
at that point we already have at least AccessShareLock, we can be sure the
column type will not change underneath us later in the query.  However,
since a backend's locks do not conflict against itself, there is still a
hole for an attacker to exploit: he could try to execute ALTER COLUMN TYPE
while a query is in progress in the current backend.  Seal that hole by
rejecting ALTER TABLE whenever the target relation is already open in
the current backend.

This is a significant security hole: not only can one trivially crash the
backend, but with appropriate misuse of pass-by-reference datatypes it is
possible to read out arbitrary locations in the server process's memory,
which could allow retrieving database content the user should not be able
to see.  Our thanks to Jeff Trout for the initial report.

Security: CVE-2007-0556
parent f8eb75b6
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.212 2007/01/25 04:35:10 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.213 2007/02/02 00:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1964,22 +1964,47 @@ update_ri_trigger_args(Oid relid,
void
AlterTable(AlterTableStmt *stmt)
{
ATController(relation_openrv(stmt->relation, AccessExclusiveLock),
stmt->cmds,
interpretInhOption(stmt->relation->inhOpt));
Relation rel = relation_openrv(stmt->relation, AccessExclusiveLock);
int expected_refcnt;
/*
* Disallow ALTER TABLE when the current backend has any open reference
* to it besides the one we just got (such as an open cursor or active
* plan); our AccessExclusiveLock doesn't protect us against stomping on
* our own foot, only other people's feet!
*
* Note: the only case known to cause serious trouble is ALTER COLUMN TYPE,
* and some changes are obviously pretty benign, so this could possibly
* be relaxed to only error out for certain types of alterations. But
* the use-case for allowing any of these things is not obvious, so we
* won't work hard at it for now.
*/
expected_refcnt = rel->rd_isnailed ? 2 : 1;
if (rel->rd_refcnt != expected_refcnt)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("relation \"%s\" is being used by active queries in this session",
RelationGetRelationName(rel))));
ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt));
}
/*
* AlterTableInternal
*
* ALTER TABLE with target specified by OID
*
* We do not reject if the relation is already open, because it's quite
* likely that one or more layers of caller have it open. That means it
* is unsafe to use this entry point for alterations that could break
* existing query plans.
*/
void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
ATController(relation_open(relid, AccessExclusiveLock),
cmds,
recurse);
Relation rel = relation_open(relid, AccessExclusiveLock);
ATController(rel, cmds, recurse);
}
static void
......@@ -2929,6 +2954,12 @@ ATSimpleRecursion(List **wqueue, Relation rel,
if (childrelid == relid)
continue;
childrel = relation_open(childrelid, AccessExclusiveLock);
/* check for child relation in use in this session */
if (childrel->rd_refcnt != 1)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("relation \"%s\" is being used by active queries in this session",
RelationGetRelationName(childrel))));
ATPrepCmd(wqueue, childrel, cmd, false, true);
relation_close(childrel, NoLock);
}
......@@ -2960,6 +2991,12 @@ ATOneLevelRecursion(List **wqueue, Relation rel,
Relation childrel;
childrel = relation_open(childrelid, AccessExclusiveLock);
/* check for child relation in use in this session */
if (childrel->rd_refcnt != 1)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("relation \"%s\" is being used by active queries in this session",
RelationGetRelationName(childrel))));
ATPrepCmd(wqueue, childrel, cmd, true, true);
relation_close(childrel, NoLock);
}
......@@ -3765,6 +3802,12 @@ ATExecDropColumn(Relation rel, const char *colName,
Form_pg_attribute childatt;
childrel = heap_open(childrelid, AccessExclusiveLock);
/* check for child relation in use in this session */
if (childrel->rd_refcnt != 1)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("relation \"%s\" is being used by active queries in this session",
RelationGetRelationName(childrel))));
tuple = SearchSysCacheCopyAttName(childrelid, colName);
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
......
......@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.285 2007/01/25 04:35:10 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.286 2007/02/02 00:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -804,7 +804,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
rliststate = (List *) ExecInitExpr((Expr *) rlist, planstate);
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rliststate, econtext, slot);
ExecBuildProjectionInfo(rliststate, econtext, slot,
resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
......
This diff is collapsed.
......@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.40 2007/01/24 01:25:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.41 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -187,7 +187,8 @@ ExecAssignScanProjectionInfo(ScanState *node)
node->ss_ScanTupleSlot->tts_tupleDescriptor))
node->ps.ps_ProjInfo = NULL;
else
ExecAssignProjectionInfo(&node->ps);
ExecAssignProjectionInfo(&node->ps,
node->ss_ScanTupleSlot->tts_tupleDescriptor);
}
static bool
......@@ -209,6 +210,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
if (!var || !IsA(var, Var))
return false; /* tlist item not a Var */
/* if these Asserts fail, planner messed up */
Assert(var->varno == varno);
Assert(var->varlevelsup == 0);
if (var->varattno != attrno)
......@@ -225,8 +227,10 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
* projection steps just to convert from specific typmod to typmod -1,
* which is pretty silly.
*/
Assert(var->vartype == att_tup->atttypid);
Assert(var->vartypmod == att_tup->atttypmod || var->vartypmod == -1);
if (var->vartype != att_tup->atttypid ||
(var->vartypmod != att_tup->atttypmod &&
var->vartypmod != -1))
return false; /* type mismatch */
tlist_item = lnext(tlist_item);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.142 2007/01/05 22:19:27 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.143 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -578,12 +578,19 @@ ExecGetResultType(PlanState *planstate)
* econtext, and storing the result into the tuple slot. (Caller must have
* ensured that tuple slot has a descriptor matching the tlist!) Note that
* the given tlist should be a list of ExprState nodes, not Expr nodes.
*
* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.
* ----------------
*/
ProjectionInfo *
ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot)
TupleTableSlot *slot,
TupleDesc inputDesc)
{
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
int len;
......@@ -598,14 +605,17 @@ ExecBuildProjectionInfo(List *targetList,
/*
* Determine whether the target list consists entirely of simple Var
* references (ie, references to non-system attributes). If so, we can
* use the simpler ExecVariableList instead of ExecTargetList.
* references (ie, references to non-system attributes) that match the
* input. If so, we can use the simpler ExecVariableList instead of
* ExecTargetList. (Note: if there is a type mismatch then ExecEvalVar
* will probably throw an error at runtime, but we leave that to it.)
*/
isVarList = true;
foreach(tl, targetList)
{
GenericExprState *gstate = (GenericExprState *) lfirst(tl);
Var *variable = (Var *) gstate->arg->expr;
Form_pg_attribute attr;
if (variable == NULL ||
!IsA(variable, Var) ||
......@@ -614,6 +624,22 @@ ExecBuildProjectionInfo(List *targetList,
isVarList = false;
break;
}
if (!inputDesc)
continue; /* can't check type, assume OK */
if (variable->varattno > inputDesc->natts)
{
isVarList = false;
break;
}
attr = inputDesc->attrs[variable->varattno - 1];
if (attr->attisdropped ||
variable->vartype != attr->atttypid ||
(variable->vartypmod != attr->atttypmod &&
variable->vartypmod != -1))
{
isVarList = false;
break;
}
}
projInfo->pi_isVarList = isVarList;
......@@ -689,15 +715,20 @@ ExecBuildProjectionInfo(List *targetList,
* ExecAssignProjectionInfo
*
* forms the projection information from the node's targetlist
*
* Notes for inputDesc are same as for ExecBuildProjectionInfo: supply it
* for a relation-scan node, can pass NULL for upper-level nodes
* ----------------
*/
void
ExecAssignProjectionInfo(PlanState *planstate)
ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc)
{
planstate->ps_ProjInfo =
ExecBuildProjectionInfo(planstate->targetlist,
planstate->ps_ExprContext,
planstate->ps_ResultTupleSlot);
planstate->ps_ResultTupleSlot,
inputDesc);
}
......
......@@ -61,7 +61,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.149 2007/01/10 18:06:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.150 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1243,7 +1243,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&aggstate->ss.ps);
ExecAssignProjectionInfo(&aggstate->ss.ps);
ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
/*
* get the count of aggregates in targetlist and quals
......
......@@ -15,7 +15,7 @@
* locate group boundaries.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.67 2007/01/10 18:06:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.68 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -205,7 +205,7 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&grpstate->ss.ps);
ExecAssignProjectionInfo(&grpstate->ss.ps);
ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
/*
* Precompute fmgr lookup data for inner loop
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.88 2007/01/30 01:33:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.89 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -431,7 +431,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&hjstate->js.ps);
ExecAssignProjectionInfo(&hjstate->js.ps);
ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
ExecGetResultType(outerPlanState(hjstate)));
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.86 2007/01/11 17:19:13 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.87 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1493,7 +1493,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&mergestate->js.ps);
ExecAssignProjectionInfo(&mergestate->js.ps);
ExecAssignProjectionInfo(&mergestate->js.ps, NULL);
/*
* preprocess the merge clauses
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.44 2007/01/05 22:19:28 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.45 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -349,7 +349,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&nlstate->js.ps);
ExecAssignProjectionInfo(&nlstate->js.ps);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
/*
* finally, wipe the current outer tuple clean.
......
......@@ -38,7 +38,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeResult.c,v 1.36 2007/01/05 22:19:28 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeResult.c,v 1.37 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -235,7 +235,7 @@ ExecInitResult(Result *node, EState *estate, int eflags)
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&resstate->ps);
ExecAssignProjectionInfo(&resstate->ps);
ExecAssignProjectionInfo(&resstate->ps, NULL);
return resstate;
}
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.83 2007/01/30 01:33:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.84 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -865,14 +865,16 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
ExecSetSlotDescriptor(slot, tupDesc);
node->projLeft = ExecBuildProjectionInfo(lefttlist,
NULL,
slot);
slot,
NULL);
tupDesc = ExecTypeFromTL(rightptlist, false);
slot = ExecAllocTableSlot(tupTable);
ExecSetSlotDescriptor(slot, tupDesc);
node->projRight = ExecBuildProjectionInfo(righttlist,
node->innerecontext,
slot);
slot,
NULL);
}
}
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.134 2007/01/10 18:06:04 tgl Exp $
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.135 2007/02/02 00:07:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -257,8 +257,10 @@ extern void ExecAssignResultTypeFromTL(PlanState *planstate);
extern TupleDesc ExecGetResultType(PlanState *planstate);
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot);
extern void ExecAssignProjectionInfo(PlanState *planstate);
TupleTableSlot *slot,
TupleDesc inputDesc);
extern void ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc);
extern void ExecFreeExprContext(PlanState *planstate);
extern TupleDesc ExecGetScanType(ScanState *scanstate);
extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment