Fix unsafe memory management in CloneRowTriggersToPartition().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 3 Jun 2019 20:59:16 +0000 (16:59 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 3 Jun 2019 20:59:26 +0000 (16:59 -0400)
It's not really supported to call systable_getnext() in a different
memory context than systable_beginscan() was called in, and it's
*definitely* not safe to do so and then reset that context between
calls.  I'm not very clear on how this code survived
CLOBBER_CACHE_ALWAYS testing ... but Alexander Lakhin found a case
that would crash it pretty reliably.

Per bug #15828.  Fix, and backpatch to v11 where this code came in.

Discussion: https://postgr.es/m/15828-f6ddd7df4852f473@postgresql.org

src/backend/commands/tablecmds.c

index e34d4ccc148071ef66e68a36eded7c110f02960d..95af5ec2dc709e05297eb2832d5f4e65dcc7255b 100644 (file)
@@ -15772,8 +15772,7 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
        ScanKeyData key;
        SysScanDesc scan;
        HeapTuple       tuple;
-       MemoryContext oldcxt,
-                               perTupCxt;
+       MemoryContext perTupCxt;
 
        ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
                                F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
@@ -15783,18 +15782,16 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 
        perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
                                                                          "clone trig", ALLOCSET_SMALL_SIZES);
-       oldcxt = MemoryContextSwitchTo(perTupCxt);
 
        while (HeapTupleIsValid(tuple = systable_getnext(scan)))
        {
-               Form_pg_trigger trigForm;
+               Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
                CreateTrigStmt *trigStmt;
                Node       *qual = NULL;
                Datum           value;
                bool            isnull;
                List       *cols = NIL;
-
-               trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
+               MemoryContext oldcxt;
 
                /*
                 * Ignore statement-level triggers; those are not cloned.
@@ -15813,6 +15810,9 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
                        elog(ERROR, "unexpected trigger \"%s\" found",
                                 NameStr(trigForm->tgname));
 
+               /* Use short-lived context for CREATE TRIGGER */
+               oldcxt = MemoryContextSwitchTo(perTupCxt);
+
                /*
                 * If there is a WHEN clause, generate a 'cooked' version of it that's
                 * appropriate for the partition.
@@ -15876,10 +15876,10 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
                                          trigForm->tgfoid, trigForm->oid, qual,
                                          false, true);
 
+               MemoryContextSwitchTo(oldcxt);
                MemoryContextReset(perTupCxt);
        }
 
-       MemoryContextSwitchTo(oldcxt);
        MemoryContextDelete(perTupCxt);
 
        systable_endscan(scan);