From 3a1db8f04f661ae2cb93b2fecf6b02277fe01fd3 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 6 Mar 2026 20:53:23 +1300
Subject: [PATCH v2 03/19] Refactor check_stack_depth() mechanism.

* The check is now inline.
* The stack is now assumed to grow in PG_STACK_DIRECTION.
* A new "soft" limit is exposed, for a later patch to use.
---
 src/backend/utils/misc/stack_depth.c | 113 +++++++++------------------
 src/include/miscadmin.h              |  53 ++++++++++++-
 2 files changed, 90 insertions(+), 76 deletions(-)

diff --git a/src/backend/utils/misc/stack_depth.c b/src/backend/utils/misc/stack_depth.c
index 3cd1b6f2336..aea589e1a27 100644
--- a/src/backend/utils/misc/stack_depth.c
+++ b/src/backend/utils/misc/stack_depth.c
@@ -25,15 +25,28 @@
 /* GUC variable for maximum stack depth (measured in kilobytes) */
 int			max_stack_depth = 100;
 
-/* max_stack_depth converted to bytes for speed of checking */
-static ssize_t max_stack_depth_bytes = 100 * (ssize_t) 1024;
-
 /*
- * Stack base pointer -- initialized by set_stack_base(), which
- * should be called from main().
+ * Thresholds -- initialized by set_stack_base().  These have external linkage
+ * so they can be used by inlined code.
  */
-static char *stack_base_ptr = NULL;
+const void *stack_base_ptr = NULL;
+const void *stack_soft_limit_ptr = NULL;
+const void *stack_hard_limit_ptr = NULL;
+
+
+static void
+compute_limit_ptrs(int kb)
+{
+	ssize_t		bytes = kb * (ssize_t) 1024;
+
+	/* Advertise a soft limit halfway through the allowed size. */
+	stack_soft_limit_ptr = (const char *) stack_base_ptr +
+		(bytes / 2) * PG_STACK_DIRECTION;
 
+	/* This is the size at which check_stack_depth() will fail. */
+	stack_hard_limit_ptr = (const char *) stack_base_ptr +
+		bytes * PG_STACK_DIRECTION;
+}
 
 /*
  * set_stack_base: set up reference point for stack depth checking
@@ -48,7 +61,7 @@ set_stack_base(void)
 #endif
 	pg_stack_base_t old;
 
-	old = stack_base_ptr;
+	old = (pg_stack_base_t) stack_base_ptr;
 
 	/*
 	 * Set up reference point for stack depth checking.  On recent gcc we use
@@ -61,6 +74,8 @@ set_stack_base(void)
 	stack_base_ptr = &stack_base;
 #endif
 
+	compute_limit_ptrs(max_stack_depth);
+
 	return old;
 }
 
@@ -76,73 +91,11 @@ set_stack_base(void)
 void
 restore_stack_base(pg_stack_base_t base)
 {
-	stack_base_ptr = base;
-}
-
-
-/*
- * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
- *
- * This should be called someplace in any recursive routine that might possibly
- * recurse deep enough to overflow the stack.  Most Unixen treat stack
- * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
- * before hitting the hardware limit.
- *
- * check_stack_depth() just throws an error summarily.  stack_is_too_deep()
- * can be used by code that wants to handle the error condition itself.
- */
-void
-check_stack_depth(void)
-{
-	if (stack_is_too_deep())
-	{
-		ereport(ERROR,
-				(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
-				 errmsg("stack depth limit exceeded"),
-				 errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
-						 "after ensuring the platform's stack depth limit is adequate.",
-						 max_stack_depth)));
-	}
-}
-
-bool
-stack_is_too_deep(void)
-{
-	char		stack_top_loc;
-	ssize_t		stack_depth;
-
-	/*
-	 * Compute distance from reference point to my local variables
-	 */
-	stack_depth = (ssize_t) (stack_base_ptr - &stack_top_loc);
+	stack_base_ptr = (const void *) base;
 
-	/*
-	 * Take abs value, since stacks grow up on some machines, down on others
-	 * (historical).  This formulation amounts to a no-op on modern systems.
-	 */
-	stack_depth *= -(PG_STACK_DIRECTION);
-
-	/*
-	 * If this assertion fails, either PG_STACK_DIRECTION is wrong or this
-	 * system doesn't have a traditional stack.
-	 */
-	Assert(stack_depth >= 0);
-
-	/*
-	 * Trouble?
-	 *
-	 * The test on stack_base_ptr prevents us from erroring out if called
-	 * before that's been set.  Logically it should be done first, but putting
-	 * it last avoids wasting cycles during normal cases.
-	 */
-	if (stack_depth > max_stack_depth_bytes &&
-		stack_base_ptr != NULL)
-		return true;
-
-	return false;
+	compute_limit_ptrs(max_stack_depth);
 }
 
-
 /* GUC check hook for max_stack_depth */
 bool
 check_max_stack_depth(int *newval, void **extra, GucSource source)
@@ -160,13 +113,25 @@ check_max_stack_depth(int *newval, void **extra, GucSource source)
 	return true;
 }
 
+/*
+ * Out-of-line part of check_stack_depth().
+ */
+void
+report_stack_is_too_deep(void)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
+			 errmsg("stack depth limit exceeded"),
+			 errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
+					 "after ensuring the platform's stack depth limit is adequate.",
+					 max_stack_depth)));
+}
+
 /* GUC assign hook for max_stack_depth */
 void
 assign_max_stack_depth(int newval, void *extra)
 {
-	ssize_t		newval_bytes = newval * (ssize_t) 1024;
-
-	max_stack_depth_bytes = newval_bytes;
+	compute_limit_ptrs(newval);
 }
 
 /*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..c618804e71f 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -293,15 +293,64 @@ extern PGDLLIMPORT bool VacuumCostActive;
 
 extern PGDLLIMPORT int max_stack_depth;
 
+extern PGDLLIMPORT const void *stack_base_ptr;
+extern PGDLLIMPORT const void *stack_soft_limit_ptr;
+extern PGDLLIMPORT const void *stack_hard_limit_ptr;
+
 /* Required daylight between max_stack_depth and the kernel limit, in bytes */
 #define STACK_DEPTH_SLOP (512 * 1024)
 
+/* Check if stack pointer p1 is deeper than p2. */
+static inline bool
+stack_ptr_deeper_p(const void *p1, const void *p2)
+{
+	const char *c1 = (const char *) p1;
+	const char *c2 = (const char *) p2;
+
+	if (PG_STACK_DIRECTION < 0)
+		return c1 < c2;
+	else
+		return c1 > c2;
+}
+
+/* Check if the stack has exceeded the configured hard limit. */
+static inline bool
+stack_is_too_deep(void)
+{
+#if pg_has_builtin(__builtin_stack_address)
+	const char *sp = (const char *) __builtin_stack_address();
+#else
+	char		c;
+	const char *sp = &c;
+#endif
+
+	return stack_ptr_deeper_p(sp, stack_hard_limit_ptr);
+}
+
+extern void report_stack_is_too_deep(void);
+
+/*
+ * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
+ *
+ * This should be called someplace in any recursive routine that might possibly
+ * recurse deep enough to overflow the stack.  Most Unixen treat stack
+ * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
+ * before hitting the hardware limit.
+ *
+ * check_stack_depth() just throws an error summarily.  stack_is_too_deep()
+ * can be used by code that wants to handle the error condition itself.
+ */
+static inline void
+check_stack_depth(void)
+{
+	if (unlikely(stack_is_too_deep()))
+		report_stack_is_too_deep();
+}
+
 typedef char *pg_stack_base_t;
 
 extern pg_stack_base_t set_stack_base(void);
 extern void restore_stack_base(pg_stack_base_t base);
-extern void check_stack_depth(void);
-extern bool stack_is_too_deep(void);
 extern ssize_t get_stack_depth_rlimit(void);
 
 /* in tcop/utility.c */
-- 
2.53.0

