Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8899a2a

Browse files
committed
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.
1 parent a09b9a3 commit 8899a2a

File tree

13 files changed

+157
-79
lines changed

13 files changed

+157
-79
lines changed

doc/src/sgml/runtime.sgml

+21-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.253 2004/03/24 03:48:41 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.254 2004/03/24 22:40:28 tgl Exp $
33
-->
44

55
<Chapter Id="runtime">
@@ -889,6 +889,26 @@ SET ENABLE_SEQSCAN TO OFF;
889889
</listitem>
890890
</varlistentry>
891891

892+
<varlistentry id="guc-max-stack-depth" xreflabel="max_stack_depth">
893+
<term><varname>max_stack_depth</varname> (<type>integer</type>)</term>
894+
<listitem>
895+
<para>
896+
Specifies the maximum safe depth of the server's execution stack.
897+
The ideal setting for this parameter is the actual stack size limit
898+
enforced by the kernel (as set by <literal>ulimit -s</> or local
899+
equivalent), less a safety margin of a megabyte or so. The safety
900+
margin is needed because the stack depth is not checked in every
901+
routine in the server, but only in key potentially-recursive routines
902+
such as expression evaluation. Setting the parameter higher than
903+
the actual kernel limit will mean that a runaway recursive function
904+
can crash an individual backend process. The default setting is
905+
2048 KB (two megabytes), which is conservatively small and unlikely
906+
to risk crashes. However, it may be too small to allow execution
907+
of complex functions.
908+
</para>
909+
</listitem>
910+
</varlistentry>
911+
892912
</variablelist>
893913
</sect3>
894914
<sect3 id="runtime-config-resource-fsm">
@@ -2573,18 +2593,6 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
25732593
</listitem>
25742594
</varlistentry>
25752595

2576-
<varlistentry id="guc-max-expr-depth" xreflabel="max_expr_depth">
2577-
<term><varname>max_expr_depth</varname> (<type>integer</type>)</term>
2578-
<listitem>
2579-
<para>
2580-
Sets the maximum expression nesting depth of the parser. The
2581-
default value of 10000 is high enough for any normal query,
2582-
but you can raise it if needed. (But if you raise it too high,
2583-
you run the risk of server crashes due to stack overflow.)
2584-
</para>
2585-
</listitem>
2586-
</varlistentry>
2587-
25882596
</variablelist>
25892597
</sect3>
25902598
</sect2>

src/backend/executor/execQual.c

+16-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.156 2004/03/17 20:48:42 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.157 2004/03/24 22:40:28 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -27,6 +27,11 @@
2727
* trying to speed it up, the execution plan should be pre-processed
2828
* to facilitate attribute sharing between nodes wherever possible,
2929
* instead of doing needless copying. -cim 5/31/91
30+
*
31+
* During expression evaluation, we check_stack_depth only in
32+
* ExecMakeFunctionResult rather than at every single node. This
33+
* is a compromise that trades off precision of the stack limit setting
34+
* to gain speed.
3035
*/
3136

3237
#include "postgres.h"
@@ -840,6 +845,9 @@ ExecMakeFunctionResult(FuncExprState *fcache,
840845
bool hasSetArg;
841846
int i;
842847

848+
/* Guard against stack overflow due to overly complex expressions */
849+
check_stack_depth();
850+
843851
/*
844852
* arguments is a list of expressions to evaluate before passing to
845853
* the function manager. We skip the evaluation if it was already
@@ -1058,6 +1066,9 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
10581066
FunctionCallInfoData fcinfo;
10591067
int i;
10601068

1069+
/* Guard against stack overflow due to overly complex expressions */
1070+
check_stack_depth();
1071+
10611072
if (isDone)
10621073
*isDone = ExprSingleResult;
10631074

@@ -2503,6 +2514,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
25032514

25042515
if (node == NULL)
25052516
return NULL;
2517+
2518+
/* Guard against stack overflow due to overly complex expressions */
2519+
check_stack_depth();
2520+
25062521
switch (nodeTag(node))
25072522
{
25082523
case T_Var:

src/backend/optimizer/util/clauses.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.166 2004/03/21 22:29:11 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.167 2004/03/24 22:40:28 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -2347,6 +2347,10 @@ expression_tree_walker(Node *node,
23472347
*/
23482348
if (node == NULL)
23492349
return false;
2350+
2351+
/* Guard against stack overflow due to overly complex expressions */
2352+
check_stack_depth();
2353+
23502354
switch (nodeTag(node))
23512355
{
23522356
case T_Var:
@@ -2720,6 +2724,10 @@ expression_tree_mutator(Node *node,
27202724

27212725
if (node == NULL)
27222726
return NULL;
2727+
2728+
/* Guard against stack overflow due to overly complex expressions */
2729+
check_stack_depth();
2730+
27232731
switch (nodeTag(node))
27242732
{
27252733
case T_Var:

src/backend/parser/parse_expr.c

+3-33
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.166 2004/03/17 20:48:42 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.167 2004/03/24 22:40:29 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -35,9 +35,6 @@
3535
#include "utils/syscache.h"
3636

3737

38-
int max_expr_depth = DEFAULT_MAX_EXPR_DEPTH;
39-
static int expr_depth_counter = 0;
40-
4138
bool Transform_null_equals = false;
4239

4340
static Node *typecast_expression(ParseState *pstate, Node *expr,
@@ -47,19 +44,6 @@ static Node *transformIndirection(ParseState *pstate, Node *basenode,
4744
List *indirection);
4845

4946

50-
/*
51-
* Initialize for parsing a new query.
52-
*
53-
* We reset the expression depth counter here, in case it was left nonzero
54-
* due to ereport()'ing out of the last parsing operation.
55-
*/
56-
void
57-
parse_expr_init(void)
58-
{
59-
expr_depth_counter = 0;
60-
}
61-
62-
6347
/*
6448
* transformExpr -
6549
* Analyze and transform expressions. Type checking and type casting is
@@ -92,20 +76,8 @@ transformExpr(ParseState *pstate, Node *expr)
9276
if (expr == NULL)
9377
return NULL;
9478

95-
/*
96-
* Guard against an overly complex expression leading to coredump due
97-
* to stack overflow here, or in later recursive routines that
98-
* traverse expression trees. Note that this is very unlikely to
99-
* happen except with pathological queries; but we don't want someone
100-
* to be able to crash the backend quite that easily...
101-
*/
102-
if (++expr_depth_counter > max_expr_depth)
103-
ereport(ERROR,
104-
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
105-
errmsg("expression too complex"),
106-
errdetail("Nesting depth exceeds maximum expression depth %d.",
107-
max_expr_depth),
108-
errhint("Increase the configuration parameter \"max_expr_depth\".")));
79+
/* Guard against stack overflow due to overly complex expressions */
80+
check_stack_depth();
10981

11082
switch (nodeTag(expr))
11183
{
@@ -938,8 +910,6 @@ transformExpr(ParseState *pstate, Node *expr)
938910
break;
939911
}
940912

941-
expr_depth_counter--;
942-
943913
return result;
944914
}
945915

src/backend/parser/parser.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Portions Copyright (c) 1994, Regents of the University of California
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/src/backend/parser/parser.c,v 1.60 2003/11/29 19:51:52 pgsql Exp $
17+
* $PostgreSQL: pgsql/src/backend/parser/parser.c,v 1.61 2004/03/24 22:40:29 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -50,7 +50,6 @@ raw_parser(const char *str)
5050

5151
scanner_init(str);
5252
parser_init();
53-
parse_expr_init();
5453

5554
yyresult = yyparse();
5655

src/backend/tcop/postgres.c

+74-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.396 2004/03/21 22:29:11 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.397 2004/03/24 22:40:29 tgl Exp $
1212
*
1313
* NOTES
1414
* this is the "main" module of the postgres backend and
@@ -92,11 +92,22 @@ bool Log_disconnections = false;
9292
*/
9393
int XfuncMode = 0;
9494

95+
/* GUC variable for maximum stack depth (measured in kilobytes) */
96+
int max_stack_depth = 2048;
97+
98+
9599
/* ----------------
96100
* private variables
97101
* ----------------
98102
*/
99103

104+
/* max_stack_depth converted to bytes for speed of checking */
105+
static int max_stack_depth_bytes = 2048*1024;
106+
107+
/* stack base pointer (initialized by PostgresMain) */
108+
static char *stack_base_ptr = NULL;
109+
110+
100111
/*
101112
* Flag to mark SIGHUP. Whenever the main loop comes around it
102113
* will reread the configuration file. (Better than doing the
@@ -1970,6 +1981,64 @@ ProcessInterrupts(void)
19701981
}
19711982

19721983

1984+
/*
1985+
* check_stack_depth: check for excessively deep recursion
1986+
*
1987+
* This should be called someplace in any recursive routine that might possibly
1988+
* recurse deep enough to overflow the stack. Most Unixen treat stack
1989+
* overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
1990+
* before hitting the hardware limit. Unfortunately we have no direct way
1991+
* to detect the hardware limit, so we have to rely on the admin to set a
1992+
* GUC variable for it ...
1993+
*/
1994+
void
1995+
check_stack_depth(void)
1996+
{
1997+
char stack_top_loc;
1998+
int stack_depth;
1999+
2000+
/*
2001+
* Compute distance from PostgresMain's local variables to my own
2002+
*
2003+
* Note: in theory stack_depth should be ptrdiff_t or some such, but
2004+
* since the whole point of this code is to bound the value to something
2005+
* much less than integer-sized, int should work fine.
2006+
*/
2007+
stack_depth = (int) (stack_base_ptr - &stack_top_loc);
2008+
/*
2009+
* Take abs value, since stacks grow up on some machines, down on others
2010+
*/
2011+
if (stack_depth < 0)
2012+
stack_depth = -stack_depth;
2013+
/*
2014+
* Trouble?
2015+
*
2016+
* The test on stack_base_ptr prevents us from erroring out if called
2017+
* during process setup or in a non-backend process. Logically it should
2018+
* be done first, but putting it here avoids wasting cycles during normal
2019+
* cases.
2020+
*/
2021+
if (stack_depth > max_stack_depth_bytes &&
2022+
stack_base_ptr != NULL)
2023+
{
2024+
ereport(ERROR,
2025+
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
2026+
errmsg("stack depth limit exceeded"),
2027+
errhint("Increase the configuration parameter \"max_stack_depth\".")));
2028+
}
2029+
}
2030+
2031+
/* GUC assign hook to update max_stack_depth_bytes from max_stack_depth */
2032+
bool
2033+
assign_max_stack_depth(int newval, bool doit, GucSource source)
2034+
{
2035+
/* Range check was already handled by guc.c */
2036+
if (doit)
2037+
max_stack_depth_bytes = newval * 1024;
2038+
return true;
2039+
}
2040+
2041+
19732042
static void
19742043
usage(char *progname)
19752044
{
@@ -2030,6 +2099,7 @@ PostgresMain(int argc, char *argv[], const char *username)
20302099
GucSource gucsource;
20312100
char *tmp;
20322101
int firstchar;
2102+
char stack_base;
20332103
StringInfoData input_message;
20342104
volatile bool send_rfq = true;
20352105

@@ -2069,6 +2139,9 @@ PostgresMain(int argc, char *argv[], const char *username)
20692139

20702140
SetProcessingMode(InitProcessing);
20712141

2142+
/* Set up reference point for stack depth checking */
2143+
stack_base_ptr = &stack_base;
2144+
20722145
/*
20732146
* Set default values for command-line options.
20742147
*/

src/backend/utils/misc/guc.c

+11-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Written by Peter Eisentraut <peter_e@gmx.net>.
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.192 2004/03/23 01:23:48 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.193 2004/03/24 22:40:29 tgl Exp $
1414
*
1515
*--------------------------------------------------------------------
1616
*/
@@ -1023,6 +1023,15 @@ static struct config_int ConfigureNamesInt[] =
10231023
16384, 1024, INT_MAX / 1024, NULL, NULL
10241024
},
10251025

1026+
{
1027+
{"max_stack_depth", PGC_SUSET, RESOURCES_MEM,
1028+
gettext_noop("Sets the maximum stack depth, in kilobytes."),
1029+
NULL
1030+
},
1031+
&max_stack_depth,
1032+
2048, 100, INT_MAX / 1024, assign_max_stack_depth, NULL
1033+
},
1034+
10261035
{
10271036
{"vacuum_cost_page_hit", PGC_USERSET, RESOURCES,
10281037
gettext_noop("Vacuum cost for a page found in the buffer cache."),
@@ -1097,14 +1106,6 @@ static struct config_int ConfigureNamesInt[] =
10971106
0, 0, INT_MAX, NULL, NULL
10981107
},
10991108
#endif
1100-
{
1101-
{"max_expr_depth", PGC_USERSET, CLIENT_CONN_OTHER,
1102-
gettext_noop("Sets the maximum expression nesting depth."),
1103-
NULL
1104-
},
1105-
&max_expr_depth,
1106-
DEFAULT_MAX_EXPR_DEPTH, 10, INT_MAX, NULL, NULL
1107-
},
11081109

11091110
{
11101111
{"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1246,7 +1247,7 @@ static struct config_int ConfigureNamesInt[] =
12461247
},
12471248

12481249
{
1249-
{"debug_shared_buffers", PGC_POSTMASTER, RESOURCES_MEM,
1250+
{"debug_shared_buffers", PGC_POSTMASTER, STATS_MONITORING,
12501251
gettext_noop("Interval to report shared buffer status in seconds"),
12511252
NULL
12521253
},

0 commit comments

Comments
 (0)