From 0df518b593a290d6d9dce11b20bcd5d6cb90f37d Mon Sep 17 00:00:00 2001 From: Satya Narlapuram Date: Tue, 12 May 2026 16:24:14 +0000 Subject: [PATCH] Fix use-after-free of propgraph orphan static lists on xact abort The static lists propgraph_orphan_label_graphids and propgraph_orphan_property_graphids in dependency.c are allocated in TopTransactionContext during deleteOneObject(). They are only reset on the success path of performDeletion()/performMultipleDeletions(), in run_deferred_propgraph_orphan_cleanup(). If a transaction aborts after the lists have been populated, for example due to statement_timeout, or any other error thrown during the cascade), AbortTransaction() frees TopTransactionContext but the static pointers remain set. A subsequent property graph cascade in the same backend session then passes the dangling pointers to list_append_unique_oid(), which walks freed memory. With assertions enabled this manifests as: TRAP: failed Assert("IsOidList(list)"), File: "list.c", Line: 726 Without assertions this silently corrupts memory. Fix by resetting both statics to NIL in the PG_FINALLY() blocks of performDeletion() and performMultipleDeletions(), which execute on both success and error paths. --- src/backend/catalog/dependency.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 489be33eb0..7c4f23d946 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -432,6 +432,18 @@ performDeletion(const ObjectAddress *object, } PG_FINALLY(); { + /* + * If we are the outermost performDeletion() and an error occurred, + * the propgraph orphan lists are allocated in TopTransactionContext + * which will be freed by AbortTransaction(). Reset the static + * pointers to NIL so that a subsequent property graph cascade in + * the same backend session does not walk freed memory. + */ + if (performDeletion_depth == 1) + { + propgraph_orphan_label_graphids = NIL; + propgraph_orphan_property_graphids = NIL; + } performDeletion_depth--; } PG_END_TRY(); @@ -574,6 +586,12 @@ performMultipleDeletions(const ObjectAddresses *objects, } PG_FINALLY(); { + /* See comment in performDeletion's PG_FINALLY block. */ + if (performDeletion_depth == 1) + { + propgraph_orphan_label_graphids = NIL; + propgraph_orphan_property_graphids = NIL; + } performDeletion_depth--; } PG_END_TRY(); -- 2.43.0