From ec47e433103099bb6c992e678b7ed925637a70d1 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Mon, 16 Dec 2024 10:11:16 -0800
Subject: [PATCH v2] radixtree: Fix crash when non-creator begins an iteration
 on shared tree.

Previously, a radix tree can be created on shared memory to share
among with other processes but processes who attached to the shared
radix tree could not begin the iteration since they don't create the
iteration context in RT_ATTACH(). It caused a server crash due to
null-pointer-dereference when a non-creator begins an iteration on
shared radix tree.

To fix it, this commit changes the iter_context to be created in
RT_BEGIN_ITERATE() in both shared and local cases.

Backpatch to v17, where radixtree.h was introduced.

Reviewed-by: John Naylor
Discussion: https://postgr.es/m/CAD21AoBB2U47V%3DF%2BwQRB1bERov_of5%3DBOZGaybjaV8FLQyqG3Q%40mail.gmail.com
Backpatch-through: 17
---
 src/include/lib/radixtree.h | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h
index 1301f3fee44..2b866e6d51c 100644
--- a/src/include/lib/radixtree.h
+++ b/src/include/lib/radixtree.h
@@ -1836,14 +1836,6 @@ RT_CREATE(MemoryContext ctx)
 	tree = (RT_RADIX_TREE *) palloc0(sizeof(RT_RADIX_TREE));
 	tree->context = ctx;
 
-	/*
-	 * Separate context for iteration in case the tree context doesn't support
-	 * pfree
-	 */
-	tree->iter_context = AllocSetContextCreate(ctx,
-											   RT_STR(RT_PREFIX) "_radix_tree iter context",
-											   ALLOCSET_SMALL_SIZES);
-
 #ifdef RT_SHMEM
 	tree->dsa = dsa;
 	dp = dsa_allocate0(dsa, sizeof(RT_RADIX_TREE_CONTROL));
@@ -1895,6 +1887,10 @@ RT_CREATE(MemoryContext ctx)
 }
 
 #ifdef RT_SHMEM
+/*
+ * Attach to an existing shared radix tree using a handle. The returned object
+ * is allocated in backend-local memory using the current memory context.
+ */
 RT_SCOPE	RT_RADIX_TREE *
 RT_ATTACH(dsa_area *dsa, RT_HANDLE handle)
 {
@@ -1902,6 +1898,7 @@ RT_ATTACH(dsa_area *dsa, RT_HANDLE handle)
 	dsa_pointer control;
 
 	tree = (RT_RADIX_TREE *) palloc0(sizeof(RT_RADIX_TREE));
+	tree->context = CurrentMemoryContext;
 
 	/* Find the control object in shared memory */
 	control = handle;
@@ -1913,10 +1910,18 @@ RT_ATTACH(dsa_area *dsa, RT_HANDLE handle)
 	return tree;
 }
 
+/*
+ * Detach from a shared radix tree. This frees backend-local resource associated
+ * with the radix tree, but the radix tree will continue to exist until it is
+ * either explicitly destroyed (by a backend that is still attached to it), or
+ * the area that backs it is returned to the operating system.
+ */
 RT_SCOPE void
 RT_DETACH(RT_RADIX_TREE * tree)
 {
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
+	if (tree->iter_context)
+		MemoryContextDelete(tree->iter_context);
 	pfree(tree);
 }
 
@@ -2086,6 +2091,15 @@ RT_BEGIN_ITERATE(RT_RADIX_TREE * tree)
 	RT_ITER    *iter;
 	RT_CHILD_PTR root;
 
+	/*
+	 * Create a separate context for iteration in case the context doesn't
+	 * support pfree, if not created yet.
+	 */
+	if (tree->iter_context == NULL)
+		tree->iter_context = AllocSetContextCreate(tree->context,
+												   RT_STR(RT_PREFIX) "_radix_tree iter context",
+												   ALLOCSET_SMALL_SIZES);
+
 	iter = (RT_ITER *) MemoryContextAllocZero(tree->iter_context,
 											  sizeof(RT_ITER));
 	iter->tree = tree;
-- 
2.43.5

