mirror of https://github.com/postgres/postgres
Previously we did this in PostmasterMain() and InitPostmasterChild(), which meant that stack depth checking was disabled in non-postmaster server processes, for instance in single-user mode. That seems like a fairly bad idea, since there's no a-priori restriction on the complexity of queries we will run in single-user mode. Moreover, this led to not having quite the same stack depth limit in all processes, which likely has no real-world effect but it offends my inner neatnik. Setting the depth in main() guarantees that check_stack_depth() is armed and has a consistent interpretation of stack depth in all forms of server processes. While at it, move the code associated with checking the stack depth out of tcop/postgres.c (which was never a great home for it) into a new file src/backend/utils/misc/stack_depth.c. Discussion: https://postgr.es/m/2081982.1734393311@sss.pgh.pa.uspull/194/head
parent
957ba9ff14
commit
c91963da13
@ -0,0 +1,197 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* stack_depth.c |
||||||
|
* Functions for monitoring and limiting process stack depth |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/backend/utils/misc/stack_depth.c |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include <limits.h> |
||||||
|
#include <sys/resource.h> |
||||||
|
|
||||||
|
#include "miscadmin.h" |
||||||
|
#include "utils/guc_hooks.h" |
||||||
|
|
||||||
|
|
||||||
|
/* 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 long max_stack_depth_bytes = 100 * 1024L; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack base pointer -- initialized by set_stack_base(), which |
||||||
|
* should be called from main(). |
||||||
|
*/ |
||||||
|
static char *stack_base_ptr = NULL; |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_stack_base: set up reference point for stack depth checking |
||||||
|
* |
||||||
|
* Returns the old reference point, if any. |
||||||
|
*/ |
||||||
|
pg_stack_base_t |
||||||
|
set_stack_base(void) |
||||||
|
{ |
||||||
|
#ifndef HAVE__BUILTIN_FRAME_ADDRESS |
||||||
|
char stack_base; |
||||||
|
#endif |
||||||
|
pg_stack_base_t old; |
||||||
|
|
||||||
|
old = stack_base_ptr; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up reference point for stack depth checking. On recent gcc we use |
||||||
|
* __builtin_frame_address() to avoid a warning about storing a local |
||||||
|
* variable's address in a long-lived variable. |
||||||
|
*/ |
||||||
|
#ifdef HAVE__BUILTIN_FRAME_ADDRESS |
||||||
|
stack_base_ptr = __builtin_frame_address(0); |
||||||
|
#else |
||||||
|
stack_base_ptr = &stack_base; |
||||||
|
#endif |
||||||
|
|
||||||
|
return old; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* restore_stack_base: restore reference point for stack depth checking |
||||||
|
* |
||||||
|
* This can be used after set_stack_base() to restore the old value. This |
||||||
|
* is currently only used in PL/Java. When PL/Java calls a backend function |
||||||
|
* from different thread, the thread's stack is at a different location than |
||||||
|
* the main thread's stack, so it sets the base pointer before the call, and |
||||||
|
* restores it afterwards. |
||||||
|
*/ |
||||||
|
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; |
||||||
|
long stack_depth; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute distance from reference point to my local variables |
||||||
|
*/ |
||||||
|
stack_depth = (long) (stack_base_ptr - &stack_top_loc); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Take abs value, since stacks grow up on some machines, down on others |
||||||
|
*/ |
||||||
|
if (stack_depth < 0) |
||||||
|
stack_depth = -stack_depth; |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* GUC check hook for max_stack_depth */ |
||||||
|
bool |
||||||
|
check_max_stack_depth(int *newval, void **extra, GucSource source) |
||||||
|
{ |
||||||
|
long newval_bytes = *newval * 1024L; |
||||||
|
long stack_rlimit = get_stack_depth_rlimit(); |
||||||
|
|
||||||
|
if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP) |
||||||
|
{ |
||||||
|
GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.", |
||||||
|
(stack_rlimit - STACK_DEPTH_SLOP) / 1024L); |
||||||
|
GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* GUC assign hook for max_stack_depth */ |
||||||
|
void |
||||||
|
assign_max_stack_depth(int newval, void *extra) |
||||||
|
{ |
||||||
|
long newval_bytes = newval * 1024L; |
||||||
|
|
||||||
|
max_stack_depth_bytes = newval_bytes; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain platform stack depth limit (in bytes) |
||||||
|
* |
||||||
|
* Return -1 if unknown |
||||||
|
*/ |
||||||
|
long |
||||||
|
get_stack_depth_rlimit(void) |
||||||
|
{ |
||||||
|
#if defined(HAVE_GETRLIMIT) |
||||||
|
static long val = 0; |
||||||
|
|
||||||
|
/* This won't change after process launch, so check just once */ |
||||||
|
if (val == 0) |
||||||
|
{ |
||||||
|
struct rlimit rlim; |
||||||
|
|
||||||
|
if (getrlimit(RLIMIT_STACK, &rlim) < 0) |
||||||
|
val = -1; |
||||||
|
else if (rlim.rlim_cur == RLIM_INFINITY) |
||||||
|
val = LONG_MAX; |
||||||
|
/* rlim_cur is probably of an unsigned type, so check for overflow */ |
||||||
|
else if (rlim.rlim_cur >= LONG_MAX) |
||||||
|
val = LONG_MAX; |
||||||
|
else |
||||||
|
val = rlim.rlim_cur; |
||||||
|
} |
||||||
|
return val; |
||||||
|
#else |
||||||
|
/* On Windows we set the backend stack size in src/backend/Makefile */ |
||||||
|
return WIN32_STACK_RLIMIT; |
||||||
|
#endif |
||||||
|
} |
Loading…
Reference in new issue