aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2021-02-27 18:09:15 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2021-02-27 18:09:15 -0300
commit262eb990c72097bd804e5c747fe38bf9b3a1ded9 (patch)
tree1ff2e93f76138db6015c62fe7e7535da37d016a5 /src/backend/commands/trigger.c
parentfb1e218cb07e9b78a26d52d1c7fc03f5ce8691eb (diff)
downloadpostgresql-262eb990c72097bd804e5c747fe38bf9b3a1ded9.tar.gz
postgresql-262eb990c72097bd804e5c747fe38bf9b3a1ded9.zip
Fix use-after-free bug with AfterTriggersTableData.storeslot
AfterTriggerSaveEvent() wrongly allocates the slot in execution-span memory context, whereas the correct thing is to allocate it in a transaction-span context, because that's where the enclosing AfterTriggersTableData instance belongs into. Backpatch to 12 (the test back to 11, where it works well with no code changes, and it's good to have to confirm that the case was previously well supported); this bug seems introduced by commit ff11e7f4b9ae. Reported-by: Bertrand Drouvot <bdrouvot@amazon.com> Author: Amit Langote <amitlangote09@gmail.com> Discussion: https://postgr.es/m/39a71864-b120-5a5c-8cc5-c632b6f16761@amazon.com
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c52
1 files changed, 33 insertions, 19 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 95752fa31d8..751c465bff6 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3832,6 +3832,8 @@ static void AfterTriggerExecute(EState *estate,
TupleTableSlot *trig_tuple_slot2);
static AfterTriggersTableData *GetAfterTriggersTableData(Oid relid,
CmdType cmdType);
+static TupleTableSlot *GetAfterTriggersStoreSlot(AfterTriggersTableData *table,
+ TupleDesc tupdesc);
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs);
static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
@@ -4634,6 +4636,31 @@ GetAfterTriggersTableData(Oid relid, CmdType cmdType)
return table;
}
+/*
+ * Returns a TupleTableSlot suitable for holding the tuples to be put
+ * into AfterTriggersTableData's transition table tuplestores.
+ */
+static TupleTableSlot *
+GetAfterTriggersStoreSlot(AfterTriggersTableData *table,
+ TupleDesc tupdesc)
+{
+ /* Create it if not already done. */
+ if (!table->storeslot)
+ {
+ MemoryContext oldcxt;
+
+ /*
+ * We only need this slot only until AfterTriggerEndQuery, but making
+ * it last till end-of-subxact is good enough. It'll be freed by
+ * AfterTriggerFreeQuery().
+ */
+ oldcxt = MemoryContextSwitchTo(CurTransactionContext);
+ table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return table->storeslot;
+}
/*
* MakeTransitionCaptureState
@@ -4922,6 +4949,8 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
table->new_tuplestore = NULL;
if (ts)
tuplestore_end(ts);
+ if (table->storeslot)
+ ExecDropSingleTupleTableSlot(table->storeslot);
}
/*
@@ -5771,17 +5800,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
if (map != NULL)
{
+ AfterTriggersTableData *table = transition_capture->tcs_private;
TupleTableSlot *storeslot;
- storeslot = transition_capture->tcs_private->storeslot;
- if (!storeslot)
- {
- storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
- map->outdesc,
- &TTSOpsVirtual);
- transition_capture->tcs_private->storeslot = storeslot;
- }
-
+ storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
execute_attr_map_slot(map->attrMap, oldslot, storeslot);
tuplestore_puttupleslot(old_tuplestore, storeslot);
}
@@ -5801,18 +5823,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
original_insert_tuple);
else if (map != NULL)
{
+ AfterTriggersTableData *table = transition_capture->tcs_private;
TupleTableSlot *storeslot;
- storeslot = transition_capture->tcs_private->storeslot;
-
- if (!storeslot)
- {
- storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
- map->outdesc,
- &TTSOpsVirtual);
- transition_capture->tcs_private->storeslot = storeslot;
- }
-
+ storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
execute_attr_map_slot(map->attrMap, newslot, storeslot);
tuplestore_puttupleslot(new_tuplestore, storeslot);
}