Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6214e2b

Browse files
committed
Fix permission checks on constraint violation errors on partitions.
If a cross-partition UPDATE violates a constraint on the target partition, and the columns in the new partition are in different physical order than in the parent, the error message can reveal columns that the user does not have SELECT permission on. A similar bug was fixed earlier in commit 804b6b6. The cause of the bug is that the callers of the ExecBuildSlotValueDescription() function got confused when constructing the list of modified columns. If the tuple was routed from a parent, we converted the tuple to the parent's format, but the list of modified columns was grabbed directly from the child's RTE entry. ExecUpdateLockMode() had a similar issue. That lead to confusion on which columns are key columns, leading to wrong tuple lock being taken on tables referenced by foreign keys, when a row is updated with INSERT ON CONFLICT UPDATE. A new isolation test is added for that corner case. With this patch, the ri_RangeTableIndex field is no longer set for partitions that don't have an entry in the range table. Previously, it was set to the RTE entry of the parent relation, but that was confusing. NOTE: This modifies the ResultRelInfo struct, replacing the ri_PartitionRoot field with ri_RootResultRelInfo. That's a bit risky to backpatch, because it breaks any extensions accessing the field. The change that ri_RangeTableIndex is not set for partitions could potentially break extensions, too. The ResultRelInfos are visible to FDWs at least, and this patch required small changes to postgres_fdw. Nevertheless, this seem like the least bad option. I don't think these fields widely used in extensions; I don't think there are FDWs out there that uses the FDW "direct update" API, other than postgres_fdw. If there is, you will get a compilation error, so hopefully it is caught quickly. Backpatch to 11, where support for both cross-partition UPDATEs, and unique indexes on partitioned tables, were added. Reviewed-by: Amit Langote Security: CVE-2021-3393
1 parent 617fffe commit 6214e2b

File tree

18 files changed

+368
-98
lines changed

18 files changed

+368
-98
lines changed

contrib/postgres_fdw/postgres_fdw.c

+15-5
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
20252025
PgFdwModifyState *fmstate;
20262026
ModifyTable *plan = castNode(ModifyTable, mtstate->ps.plan);
20272027
EState *estate = mtstate->ps.state;
2028-
Index resultRelation = resultRelInfo->ri_RangeTableIndex;
2028+
Index resultRelation;
20292029
Relation rel = resultRelInfo->ri_RelationDesc;
20302030
RangeTblEntry *rte;
20312031
TupleDesc tupdesc = RelationGetDescr(rel);
@@ -2077,17 +2077,20 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
20772077
}
20782078

20792079
/*
2080-
* If the foreign table is a partition, we need to create a new RTE
2080+
* If the foreign table is a partition that doesn't have a corresponding
2081+
* RTE entry, we need to create a new RTE
20812082
* describing the foreign table for use by deparseInsertSql and
20822083
* create_foreign_modify() below, after first copying the parent's RTE and
20832084
* modifying some fields to describe the foreign partition to work on.
20842085
* However, if this is invoked by UPDATE, the existing RTE may already
20852086
* correspond to this partition if it is one of the UPDATE subplan target
20862087
* rels; in that case, we can just use the existing RTE as-is.
20872088
*/
2088-
rte = exec_rt_fetch(resultRelation, estate);
2089-
if (rte->relid != RelationGetRelid(rel))
2089+
if (resultRelInfo->ri_RangeTableIndex == 0)
20902090
{
2091+
ResultRelInfo *rootResultRelInfo = resultRelInfo->ri_RootResultRelInfo;
2092+
2093+
rte = exec_rt_fetch(rootResultRelInfo->ri_RangeTableIndex, estate);
20912094
rte = copyObject(rte);
20922095
rte->relid = RelationGetRelid(rel);
20932096
rte->relkind = RELKIND_FOREIGN_TABLE;
@@ -2099,8 +2102,15 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
20992102
* Vars contained in those expressions.
21002103
*/
21012104
if (plan && plan->operation == CMD_UPDATE &&
2102-
resultRelation == plan->rootRelation)
2105+
rootResultRelInfo->ri_RangeTableIndex == plan->rootRelation)
21032106
resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
2107+
else
2108+
resultRelation = rootResultRelInfo->ri_RangeTableIndex;
2109+
}
2110+
else
2111+
{
2112+
resultRelation = resultRelInfo->ri_RangeTableIndex;
2113+
rte = exec_rt_fetch(resultRelation, estate);
21042114
}
21052115

21062116
/* Construct the SQL command string. */

src/backend/access/common/tupconvert.c

+51
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,57 @@ execute_attr_map_slot(AttrMap *attrMap,
226226
return out_slot;
227227
}
228228

229+
/*
230+
* Perform conversion of bitmap of columns according to the map.
231+
*
232+
* The input and output bitmaps are offset by
233+
* FirstLowInvalidHeapAttributeNumber to accommodate system cols, like the
234+
* column-bitmaps in RangeTblEntry.
235+
*/
236+
Bitmapset *
237+
execute_attr_map_cols(AttrMap *attrMap, Bitmapset *in_cols)
238+
{
239+
Bitmapset *out_cols;
240+
int out_attnum;
241+
242+
/* fast path for the common trivial case */
243+
if (in_cols == NULL)
244+
return NULL;
245+
246+
/*
247+
* For each output column, check which input column it corresponds to.
248+
*/
249+
out_cols = NULL;
250+
251+
for (out_attnum = FirstLowInvalidHeapAttributeNumber;
252+
out_attnum <= attrMap->maplen;
253+
out_attnum++)
254+
{
255+
int in_attnum;
256+
257+
if (out_attnum < 0)
258+
{
259+
/* System column. No mapping. */
260+
in_attnum = out_attnum;
261+
}
262+
else if (out_attnum == 0)
263+
continue;
264+
else
265+
{
266+
/* normal user column */
267+
in_attnum = attrMap->attnums[out_attnum - 1];
268+
269+
if (in_attnum == 0)
270+
continue;
271+
}
272+
273+
if (bms_is_member(in_attnum - FirstLowInvalidHeapAttributeNumber, in_cols))
274+
out_cols = bms_add_member(out_cols, out_attnum - FirstLowInvalidHeapAttributeNumber);
275+
}
276+
277+
return out_cols;
278+
}
279+
229280
/*
230281
* Free a TupleConversionMap structure.
231282
*/

src/backend/commands/copyfrom.c

+1
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ CopyFrom(CopyFromState cstate)
666666
mtstate->ps.state = estate;
667667
mtstate->operation = CMD_INSERT;
668668
mtstate->resultRelInfo = resultRelInfo;
669+
mtstate->rootResultRelInfo = resultRelInfo;
669670

670671
if (resultRelInfo->ri_FdwRoutine != NULL &&
671672
resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)

src/backend/commands/explain.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3694,7 +3694,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
36943694
/* Should we explicitly label target relations? */
36953695
labeltargets = (mtstate->mt_nplans > 1 ||
36963696
(mtstate->mt_nplans == 1 &&
3697-
mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation));
3697+
mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation));
36983698

36993699
if (labeltargets)
37003700
ExplainOpenGroup("Target Tables", "Target Tables", false, es);

src/backend/commands/trigger.c

+10-12
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,6 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
7070
/* How many levels deep into trigger execution are we? */
7171
static int MyTriggerDepth = 0;
7272

73-
/*
74-
* The authoritative version of this macro is in executor/execMain.c. Be sure
75-
* to keep everything in sync.
76-
*/
77-
#define GetAllUpdatedColumns(relinfo, estate) \
78-
(bms_union(exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols, \
79-
exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->extraUpdatedCols))
80-
8173
/* Local function prototypes */
8274
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger);
8375
static bool GetTupleForTrigger(EState *estate,
@@ -2643,7 +2635,10 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
26432635
CMD_UPDATE))
26442636
return;
26452637

2646-
updatedCols = GetAllUpdatedColumns(relinfo, estate);
2638+
/* statement-level triggers operate on the parent table */
2639+
Assert(relinfo->ri_RootResultRelInfo == NULL);
2640+
2641+
updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
26472642

26482643
LocTriggerData.type = T_TriggerData;
26492644
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
@@ -2684,10 +2679,13 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
26842679
{
26852680
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
26862681

2682+
/* statement-level triggers operate on the parent table */
2683+
Assert(relinfo->ri_RootResultRelInfo == NULL);
2684+
26872685
if (trigdesc && trigdesc->trig_update_after_statement)
26882686
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
26892687
false, NULL, NULL, NIL,
2690-
GetAllUpdatedColumns(relinfo, estate),
2688+
ExecGetAllUpdatedCols(relinfo, estate),
26912689
transition_capture);
26922690
}
26932691

@@ -2757,7 +2755,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
27572755
TRIGGER_EVENT_ROW |
27582756
TRIGGER_EVENT_BEFORE;
27592757
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2760-
updatedCols = GetAllUpdatedColumns(relinfo, estate);
2758+
updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
27612759
LocTriggerData.tg_updatedcols = updatedCols;
27622760
for (i = 0; i < trigdesc->numtriggers; i++)
27632761
{
@@ -2858,7 +2856,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
28582856

28592857
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
28602858
true, oldslot, newslot, recheckIndexes,
2861-
GetAllUpdatedColumns(relinfo, estate),
2859+
ExecGetAllUpdatedCols(relinfo, estate),
28622860
transition_capture);
28632861
}
28642862
}

src/backend/executor/execIndexing.c

+2-11
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,6 @@ typedef enum
124124
CEOUC_LIVELOCK_PREVENTING_WAIT
125125
} CEOUC_WAIT_MODE;
126126

127-
/*
128-
* The authoritative version of these macro are in executor/execMain.c. Be
129-
* sure to keep everything in sync.
130-
*/
131-
#define GetUpdatedColumns(relinfo, estate) \
132-
(exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols)
133-
#define GetExtraUpdatedColumns(relinfo, estate) \
134-
(exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->extraUpdatedCols)
135-
136127
static bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
137128
IndexInfo *indexInfo,
138129
ItemPointer tupleid,
@@ -944,8 +935,8 @@ static bool
944935
index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate,
945936
IndexInfo *indexInfo, Relation indexRelation)
946937
{
947-
Bitmapset *updatedCols = GetUpdatedColumns(resultRelInfo, estate);
948-
Bitmapset *extraUpdatedCols = GetExtraUpdatedColumns(resultRelInfo, estate);
938+
Bitmapset *updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
939+
Bitmapset *extraUpdatedCols = ExecGetExtraUpdatedCols(resultRelInfo, estate);
949940
Bitmapset *allUpdatedCols;
950941
bool hasexpression = false;
951942
List *idxExprs;

0 commit comments

Comments
 (0)