mirror of
https://github.com/postgres/postgres.git
synced 2025-06-17 17:02:08 +03:00
Set the stack_base_ptr in main(), not in random other places.
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.us
This commit is contained in:
@ -94,9 +94,6 @@ bool Log_disconnections = false;
|
||||
|
||||
int log_statement = LOGSTMT_NONE;
|
||||
|
||||
/* GUC variable for maximum stack depth (measured in kilobytes) */
|
||||
int max_stack_depth = 100;
|
||||
|
||||
/* wait N seconds to allow attach from a debugger */
|
||||
int PostAuthDelay = 0;
|
||||
|
||||
@ -124,15 +121,6 @@ typedef struct BindParamCbData
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* max_stack_depth converted to bytes for speed of checking */
|
||||
static long max_stack_depth_bytes = 100 * 1024L;
|
||||
|
||||
/*
|
||||
* Stack base pointer -- initialized by PostmasterMain and inherited by
|
||||
* subprocesses (but see also InitPostmasterChild).
|
||||
*/
|
||||
static char *stack_base_ptr = NULL;
|
||||
|
||||
/*
|
||||
* Flag to keep track of whether we have started a transaction.
|
||||
* For extended query protocol this has to be remembered across messages.
|
||||
@ -3513,133 +3501,6 @@ ProcessInterrupts(void)
|
||||
HandleParallelApplyMessages();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* during process setup or in a non-backend process. Logically it should
|
||||
* be done first, but putting it here 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* GUC check_hook for client_connection_check_interval
|
||||
*/
|
||||
@ -5099,40 +4960,6 @@ forbidden_in_wal_sender(char firstchar)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
|
||||
|
||||
static struct rusage Save_r;
|
||||
static struct timeval Save_t;
|
||||
|
||||
|
Reference in New Issue
Block a user