From 8899a2aba92c4a17f422172e7c9dd0e383eefa39 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 24 Mar 2004 22:40:29 +0000 Subject: Replace max_expr_depth parameter with a max_stack_depth parameter that is measured in kilobytes and checked against actual physical execution stack depth, as per my proposal of 30-Dec. This gives us a fairly bulletproof defense against crashing due to runaway recursive functions. --- src/backend/tcop/postgres.c | 75 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'src/backend/tcop/postgres.c') diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 91442d49e6e..ff0ac6aa64a 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.396 2004/03/21 22:29:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.397 2004/03/24 22:40:29 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -92,11 +92,22 @@ bool Log_disconnections = false; */ int XfuncMode = 0; +/* GUC variable for maximum stack depth (measured in kilobytes) */ +int max_stack_depth = 2048; + + /* ---------------- * private variables * ---------------- */ +/* max_stack_depth converted to bytes for speed of checking */ +static int max_stack_depth_bytes = 2048*1024; + +/* stack base pointer (initialized by PostgresMain) */ +static char *stack_base_ptr = NULL; + + /* * Flag to mark SIGHUP. Whenever the main loop comes around it * will reread the configuration file. (Better than doing the @@ -1970,6 +1981,64 @@ ProcessInterrupts(void) } +/* + * check_stack_depth: 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. Unfortunately we have no direct way + * to detect the hardware limit, so we have to rely on the admin to set a + * GUC variable for it ... + */ +void +check_stack_depth(void) +{ + char stack_top_loc; + int stack_depth; + + /* + * Compute distance from PostgresMain's local variables to my own + * + * Note: in theory stack_depth should be ptrdiff_t or some such, but + * since the whole point of this code is to bound the value to something + * much less than integer-sized, int should work fine. + */ + stack_depth = (int) (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) + { + ereport(ERROR, + (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), + errmsg("stack depth limit exceeded"), + errhint("Increase the configuration parameter \"max_stack_depth\"."))); + } +} + +/* GUC assign hook to update max_stack_depth_bytes from max_stack_depth */ +bool +assign_max_stack_depth(int newval, bool doit, GucSource source) +{ + /* Range check was already handled by guc.c */ + if (doit) + max_stack_depth_bytes = newval * 1024; + return true; +} + + static void usage(char *progname) { @@ -2030,6 +2099,7 @@ PostgresMain(int argc, char *argv[], const char *username) GucSource gucsource; char *tmp; int firstchar; + char stack_base; StringInfoData input_message; volatile bool send_rfq = true; @@ -2069,6 +2139,9 @@ PostgresMain(int argc, char *argv[], const char *username) SetProcessingMode(InitProcessing); + /* Set up reference point for stack depth checking */ + stack_base_ptr = &stack_base; + /* * Set default values for command-line options. */ -- cgit v1.2.3