mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
This change adapts these functions to the machine's address width without depending on "long" to be the right size. (It isn't on Win64, for example.) While it seems unlikely anyone would care to run with a stack depth limit exceeding 2GB, this is part of a general push to avoid using type "long" to represent memory sizes. It's convenient to use ssize_t rather than the perhaps-more-obvious choice of size_t/Size, because the code involved depends on working with a signed data type. Our MAX_KILOBYTES limit already ensures that ssize_t will be sufficient to represent the maximum value of max_stack_depth. Extracted from a larger patch by Vladlen, plus additional hackery by me. Author: Vladlen Popolitov <v.popolitov@postgrespro.ru> Author: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/1a01f0-66ec2d80-3b-68487680@27595217
202 lines
5.1 KiB
C
202 lines
5.1 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* stack_depth.c
|
|
* Functions for monitoring and limiting process stack depth
|
|
*
|
|
* Portions Copyright (c) 1996-2025, 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 ssize_t max_stack_depth_bytes = 100 * (ssize_t) 1024;
|
|
|
|
/*
|
|
* 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;
|
|
ssize_t stack_depth;
|
|
|
|
/*
|
|
* Compute distance from reference point to my local variables
|
|
*/
|
|
stack_depth = (ssize_t) (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)
|
|
{
|
|
ssize_t newval_bytes = *newval * (ssize_t) 1024;
|
|
ssize_t 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 %zdkB.",
|
|
(stack_rlimit - STACK_DEPTH_SLOP) / 1024);
|
|
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)
|
|
{
|
|
ssize_t newval_bytes = newval * (ssize_t) 1024;
|
|
|
|
max_stack_depth_bytes = newval_bytes;
|
|
}
|
|
|
|
/*
|
|
* Obtain platform stack depth limit (in bytes)
|
|
*
|
|
* Return -1 if unknown
|
|
*
|
|
* Note: we choose to use ssize_t not size_t as the result type because
|
|
* callers compute values that could theoretically go negative,
|
|
* such as "result - STACK_DEPTH_SLOP".
|
|
*/
|
|
ssize_t
|
|
get_stack_depth_rlimit(void)
|
|
{
|
|
#if defined(HAVE_GETRLIMIT)
|
|
static ssize_t 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 = SSIZE_MAX;
|
|
/* rlim_cur is probably of an unsigned type, so check for overflow */
|
|
else if (rlim.rlim_cur >= SSIZE_MAX)
|
|
val = SSIZE_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
|
|
}
|