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-2025, 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 3330 : 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 3330 : 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 3330 : stack_base_ptr = __builtin_frame_address(0); 60 : #else 61 : stack_base_ptr = &stack_base; 62 : #endif 63 : 64 3330 : 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 577402304 : check_stack_depth(void) 96 : { 97 577402304 : if (stack_is_too_deep()) 98 : { 99 30 : 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 577402274 : } 107 : 108 : bool 109 599171540 : 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 599171540 : 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 599171540 : 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 599171540 : if (stack_depth > max_stack_depth_bytes && 133 30 : stack_base_ptr != NULL) 134 30 : return true; 135 : 136 599171510 : return false; 137 : } 138 : 139 : 140 : /* GUC check hook for max_stack_depth */ 141 : bool 142 10258 : check_max_stack_depth(int *newval, void **extra, GucSource source) 143 : { 144 10258 : ssize_t newval_bytes = *newval * (ssize_t) 1024; 145 10258 : ssize_t stack_rlimit = get_stack_depth_rlimit(); 146 : 147 10258 : 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 10258 : return true; 155 : } 156 : 157 : /* GUC assign hook for max_stack_depth */ 158 : void 159 10266 : assign_max_stack_depth(int newval, void *extra) 160 : { 161 10266 : ssize_t newval_bytes = newval * (ssize_t) 1024; 162 : 163 10266 : max_stack_depth_bytes = newval_bytes; 164 10266 : } 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 13080 : 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 13080 : if (val == 0) 183 : { 184 : struct rlimit rlim; 185 : 186 1992 : if (getrlimit(RLIMIT_STACK, &rlim) < 0) 187 0 : val = -1; 188 1992 : 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 1992 : else if (rlim.rlim_cur >= SSIZE_MAX) 192 0 : val = SSIZE_MAX; 193 : else 194 1992 : val = rlim.rlim_cur; 195 : } 196 13080 : 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 : }