From 0df518b593a290d6d9dce11b20bcd5d6cb90f37d Mon Sep 17 00:00:00 2001
From: Satya Narlapuram <satyanarlapuram@gmail.com>
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

