From 01d6ba83347279ea53ffc95dc000dd906a4d64f0 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 v1] 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(). To avoid null-pointer-dereference
crash, this commit creates the iter_context in RT_ATTACH().

Backpatch to v17, where radixtree.h was introduced.

Reviewed-by:
Discussion: https://postgr.es/m/
Backpatch-through: 17
---
 src/include/lib/radixtree.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h
index 1301f3fee44..39fc56cb9fb 100644
--- a/src/include/lib/radixtree.h
+++ b/src/include/lib/radixtree.h
@@ -1895,6 +1895,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 +1906,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;
@@ -1910,13 +1915,28 @@ RT_ATTACH(dsa_area *dsa, RT_HANDLE handle)
 	tree->ctl = (RT_RADIX_TREE_CONTROL *) dsa_get_address(dsa, control);
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
 
+	/*
+	 * Create the iteration context so that the attached backend also can
+	 * begin the iteration.
+	 */
+	tree->iter_context = AllocSetContextCreate(CurrentMemoryContext,
+											   RT_STR(RT_PREFIX) "_radix_tree iter context",
+											   ALLOCSET_SMALL_SIZES);
+
 	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);
+	MemoryContextDelete(tree->iter_context);
 	pfree(tree);
 }
 
-- 
2.43.5

