Fix potential use-after-free for BEFORE UPDATE row triggers on non-core AMs.
authorAndres Freund <andres@anarazel.de>
Fri, 19 Apr 2019 00:53:54 +0000 (17:53 -0700)
committerAndres Freund <andres@anarazel.de>
Fri, 19 Apr 2019 00:53:54 +0000 (17:53 -0700)
When such a trigger returns the old row version, it naturally get
stored in the slot for the trigger result. When a table AMs doesn't
store HeapTuples internally, ExecBRUpdateTriggers() frees the old row
version passed to triggers - but before this fix it might still be
referenced by the slot holding the new tuple.

Noticed when running the out-of-core zheap AM against the in-core
version of tableam.

Author: Andres Freund

src/backend/commands/trigger.c

index 3ae2640abd4cd9b22b8a2bdad9ed489713101780..c81ccc8fcffc9108594aa40530107595cecb606f 100644 (file)
@@ -3109,6 +3109,15 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                {
                        ExecForceStoreHeapTuple(newtuple, newslot);
 
+                       /*
+                        * If the tuple returned by the trigger / being stored, is the old
+                        * row version, and the heap tuple passed to the trigger was
+                        * allocated locally, materialize the slot. Otherwise we might
+                        * free it while still referenced by the slot.
+                        */
+                       if (should_free_trig && newtuple == trigtuple)
+                               ExecMaterializeSlot(newslot);
+
                        if (should_free_new)
                                heap_freetuple(oldtuple);