Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * stack_depth.c
4 : * Functions for monitoring and limiting process stack depth
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/misc/stack_depth.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include <limits.h>
19 : #include <sys/resource.h>
20 :
21 : #include "miscadmin.h"
22 : #include "utils/guc_hooks.h"
23 :
24 :
25 : /* GUC variable for maximum stack depth (measured in kilobytes) */
26 : int max_stack_depth = 100;
27 :
28 : /* max_stack_depth converted to bytes for speed of checking */
29 : static ssize_t max_stack_depth_bytes = 100 * (ssize_t) 1024;
30 :
31 : /*
32 : * Stack base pointer -- initialized by set_stack_base(), which
33 : * should be called from main().
34 : */
35 : static char *stack_base_ptr = NULL;
36 :
37 :
38 : /*
39 : * set_stack_base: set up reference point for stack depth checking
40 : *
41 : * Returns the old reference point, if any.
42 : */
43 : pg_stack_base_t
44 2010 : set_stack_base(void)
45 : {
46 : #ifndef HAVE__BUILTIN_FRAME_ADDRESS
47 : char stack_base;
48 : #endif
49 : pg_stack_base_t old;
50 :
51 2010 : old = stack_base_ptr;
52 :
53 : /*
54 : * Set up reference point for stack depth checking. On recent gcc we use
55 : * __builtin_frame_address() to avoid a warning about storing a local
56 : * variable's address in a long-lived variable.
57 : */
58 : #ifdef HAVE__BUILTIN_FRAME_ADDRESS
59 2010 : stack_base_ptr = __builtin_frame_address(0);
60 : #else
61 : stack_base_ptr = &stack_base;
62 : #endif
63 :
64 2010 : return old;
65 : }
66 :
67 : /*
68 : * restore_stack_base: restore reference point for stack depth checking
69 : *
70 : * This can be used after set_stack_base() to restore the old value. This
71 : * is currently only used in PL/Java. When PL/Java calls a backend function
72 : * from different thread, the thread's stack is at a different location than
73 : * the main thread's stack, so it sets the base pointer before the call, and
74 : * restores it afterwards.
75 : */
76 : void
77 0 : restore_stack_base(pg_stack_base_t base)
78 : {
79 0 : stack_base_ptr = base;
80 0 : }
81 :
82 :
83 : /*
84 : * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
85 : *
86 : * This should be called someplace in any recursive routine that might possibly
87 : * recurse deep enough to overflow the stack. Most Unixen treat stack
88 : * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
89 : * before hitting the hardware limit.
90 : *
91 : * check_stack_depth() just throws an error summarily. stack_is_too_deep()
92 : * can be used by code that wants to handle the error condition itself.
93 : */
94 : void
95 346150849 : check_stack_depth(void)
96 : {
97 346150849 : if (stack_is_too_deep())
98 : {
99 15 : ereport(ERROR,
100 : (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
101 : errmsg("stack depth limit exceeded"),
102 : errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
103 : "after ensuring the platform's stack depth limit is adequate.",
104 : max_stack_depth)));
105 : }
106 346150834 : }
107 :
108 : bool
109 357170327 : stack_is_too_deep(void)
110 : {
111 : char stack_top_loc;
112 : ssize_t stack_depth;
113 :
114 : /*
115 : * Compute distance from reference point to my local variables
116 : */
117 357170327 : stack_depth = (ssize_t) (stack_base_ptr - &stack_top_loc);
118 :
119 : /*
120 : * Take abs value, since stacks grow up on some machines, down on others
121 : */
122 357170327 : if (stack_depth < 0)
123 0 : stack_depth = -stack_depth;
124 :
125 : /*
126 : * Trouble?
127 : *
128 : * The test on stack_base_ptr prevents us from erroring out if called
129 : * before that's been set. Logically it should be done first, but putting
130 : * it last avoids wasting cycles during normal cases.
131 : */
132 357170327 : if (stack_depth > max_stack_depth_bytes &&
133 15 : stack_base_ptr != NULL)
134 15 : return true;
135 :
136 357170312 : return false;
137 : }
138 :
139 :
140 : /* GUC check hook for max_stack_depth */
141 : bool
142 6148 : check_max_stack_depth(int *newval, void **extra, GucSource source)
143 : {
144 6148 : ssize_t newval_bytes = *newval * (ssize_t) 1024;
145 6148 : ssize_t stack_rlimit = get_stack_depth_rlimit();
146 :
147 6148 : if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
148 : {
149 0 : GUC_check_errdetail("\"max_stack_depth\" must not exceed %zdkB.",
150 0 : (stack_rlimit - STACK_DEPTH_SLOP) / 1024);
151 0 : GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.");
152 0 : return false;
153 : }
154 6148 : return true;
155 : }
156 :
157 : /* GUC assign hook for max_stack_depth */
158 : void
159 6152 : assign_max_stack_depth(int newval, void *extra)
160 : {
161 6152 : ssize_t newval_bytes = newval * (ssize_t) 1024;
162 :
163 6152 : max_stack_depth_bytes = newval_bytes;
164 6152 : }
165 :
166 : /*
167 : * Obtain platform stack depth limit (in bytes)
168 : *
169 : * Return -1 if unknown
170 : *
171 : * Note: we choose to use ssize_t not size_t as the result type because
172 : * callers compute values that could theoretically go negative,
173 : * such as "result - STACK_DEPTH_SLOP".
174 : */
175 : ssize_t
176 8126 : get_stack_depth_rlimit(void)
177 : {
178 : #if defined(HAVE_GETRLIMIT)
179 : static ssize_t val = 0;
180 :
181 : /* This won't change after process launch, so check just once */
182 8126 : if (val == 0)
183 : {
184 : struct rlimit rlim;
185 :
186 1188 : if (getrlimit(RLIMIT_STACK, &rlim) < 0)
187 0 : val = -1;
188 1188 : else if (rlim.rlim_cur == RLIM_INFINITY)
189 0 : val = SSIZE_MAX;
190 : /* rlim_cur is probably of an unsigned type, so check for overflow */
191 1188 : else if (rlim.rlim_cur >= SSIZE_MAX)
192 0 : val = SSIZE_MAX;
193 : else
194 1188 : val = rlim.rlim_cur;
195 : }
196 8126 : return val;
197 : #else
198 : /* On Windows we set the backend stack size in src/backend/Makefile */
199 : return WIN32_STACK_RLIMIT;
200 : #endif
201 : }
|