On platforms that have getrlimit(RLIMIT_STACK), use it to ensure that

max_stack_depth is not set to an unsafe value.

This commit also provides configure-time checking for <sys/resource.h>,
and cleans up some perhaps-unportable code associated with use of that
include file and getrlimit().
This commit is contained in:
Tom Lane 2006-10-07 19:25:29 +00:00
parent 1c160291ef
commit 71a6f8b85b
9 changed files with 136 additions and 52 deletions

6
configure vendored
View file

@ -7589,7 +7589,8 @@ done
for ac_header in crypt.h dld.h endian.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/time.h sys/un.h termios.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h
for ac_header in crypt.h dld.h endian.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/time.h sys/un.h termios.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h
do do
as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
if eval "test \"\${$as_ac_Header+set}\" = set"; then if eval "test \"\${$as_ac_Header+set}\" = set"; then
@ -13454,7 +13455,8 @@ fi
for ac_func in cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
for ac_func in cbrt dlopen fcvt fdatasync getpeereid getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
do do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
echo "$as_me:$LINENO: checking for $ac_func" >&5 echo "$as_me:$LINENO: checking for $ac_func" >&5

View file

@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
dnl $PostgreSQL: pgsql/configure.in,v 1.481 2006/10/05 00:07:45 tgl Exp $ dnl $PostgreSQL: pgsql/configure.in,v 1.482 2006/10/07 19:25:28 tgl Exp $
dnl dnl
dnl Developers, please strive to achieve this order: dnl Developers, please strive to achieve this order:
dnl dnl
@ -717,7 +717,7 @@ fi
## ##
dnl sys/socket.h is required by AC_FUNC_ACCEPT_ARGTYPES dnl sys/socket.h is required by AC_FUNC_ACCEPT_ARGTYPES
AC_CHECK_HEADERS([crypt.h dld.h endian.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/time.h sys/un.h termios.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h]) AC_CHECK_HEADERS([crypt.h dld.h endian.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/time.h sys/un.h termios.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h])
# At least on IRIX, cpp test for netinet/tcp.h will fail unless # At least on IRIX, cpp test for netinet/tcp.h will fail unless
# netinet/in.h is included first. # netinet/in.h is included first.
@ -865,7 +865,7 @@ PGAC_VAR_INT_TIMEZONE
AC_FUNC_ACCEPT_ARGTYPES AC_FUNC_ACCEPT_ARGTYPES
PGAC_FUNC_GETTIMEOFDAY_1ARG PGAC_FUNC_GETTIMEOFDAY_1ARG
AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs]) AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>]) AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
AC_CHECK_DECLS(posix_fadvise, [], [], [#include <fcntl.h>]) AC_CHECK_DECLS(posix_fadvise, [], [], [#include <fcntl.h>])

View file

@ -1,5 +1,5 @@
/* /*
* $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.56 2006/10/04 00:29:45 momjian Exp $ * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.57 2006/10/07 19:25:28 tgl Exp $
* *
* pgbench: a simple benchmark program for PostgreSQL * pgbench: a simple benchmark program for PostgreSQL
* written by Tatsuo Ishii * written by Tatsuo Ishii
@ -37,8 +37,9 @@
#include <sys/select.h> #include <sys/select.h>
#endif #endif
/* for getrlimit */ #ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h> #include <sys/resource.h> /* for getrlimit */
#endif
#endif /* ! WIN32 */ #endif /* ! WIN32 */
extern char *optarg; extern char *optarg;
@ -1172,7 +1173,7 @@ main(int argc, char **argv)
int nsocks; /* return from select(2) */ int nsocks; /* return from select(2) */
int maxsock; /* max socket number to be waited */ int maxsock; /* max socket number to be waited */
#if !(defined(__CYGWIN__) || defined(__MINGW32__)) #ifdef HAVE_GETRLIMIT
struct rlimit rlim; struct rlimit rlim;
#endif #endif
@ -1233,8 +1234,8 @@ main(int argc, char **argv)
fprintf(stderr, "invalid number of clients: %d\n", nclients); fprintf(stderr, "invalid number of clients: %d\n", nclients);
exit(1); exit(1);
} }
#if !(defined(__CYGWIN__) || defined(__MINGW32__)) #ifdef HAVE_GETRLIMIT
#ifdef RLIMIT_NOFILE /* most platform uses RLIMIT_NOFILE */ #ifdef RLIMIT_NOFILE /* most platforms use RLIMIT_NOFILE */
if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
#else /* but BSD doesn't ... */ #else /* but BSD doesn't ... */
if (getrlimit(RLIMIT_OFILE, &rlim) == -1) if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
@ -1245,11 +1246,11 @@ main(int argc, char **argv)
} }
if (rlim.rlim_cur <= (nclients + 2)) if (rlim.rlim_cur <= (nclients + 2))
{ {
fprintf(stderr, "You need at least %d open files resource but you are only allowed to use %ld.\n", nclients + 2, (long) rlim.rlim_cur); fprintf(stderr, "You need at least %d open files but you are only allowed to use %ld.\n", nclients + 2, (long) rlim.rlim_cur);
fprintf(stderr, "Use limit/ulimt to increase the limit before using pgbench.\n"); fprintf(stderr, "Use limit/ulimit to increase the limit before using pgbench.\n");
exit(1); exit(1);
} }
#endif #endif /* HAVE_GETRLIMIT */
break; break;
case 'C': case 'C':
is_connect = 1; is_connect = 1;

View file

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.89 2006/09/25 22:12:24 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.90 2006/10/07 19:25:28 tgl Exp $ -->
<chapter Id="runtime-config"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
@ -846,14 +846,22 @@ SET ENABLE_SEQSCAN TO OFF;
equivalent), less a safety margin of a megabyte or so. The safety equivalent), less a safety margin of a megabyte or so. The safety
margin is needed because the stack depth is not checked in every margin is needed because the stack depth is not checked in every
routine in the server, but only in key potentially-recursive routines routine in the server, but only in key potentially-recursive routines
such as expression evaluation. Setting the parameter higher than such as expression evaluation. The default setting is
the actual kernel limit will mean that a runaway recursive function
can crash an individual backend process. The default setting is
2048 KB (two megabytes), which is conservatively small and unlikely 2048 KB (two megabytes), which is conservatively small and unlikely
to risk crashes. However, it may be too small to allow execution to risk crashes. However, it may be too small to allow execution
of complex functions. of complex functions.
Only superusers can change this setting. Only superusers can change this setting.
</para> </para>
<para>
Setting <varname>max_stack_depth</> higher than
the actual kernel limit will mean that a runaway recursive function
can crash an individual backend process. On platforms where
<productname>PostgreSQL</productname> can determine the kernel limit,
it will not let you set this variable to an unsafe value. However,
not all platforms provide the information, so caution is recommended
in selecting a value.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -2173,19 +2181,6 @@ SELECT * FROM parent WHERE key = 2400;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-gin-fuzzy-search-limit" xreflabel="gin_fuzzy_search_limit">
<term><varname>gin_fuzzy_search_limit</varname> (<type>integer</type>)</term>
<indexterm>
<primary><varname>gin_fuzzy_search_limit</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Soft upper limit of the size of the set returned by GIN index. For more
information see <xref linkend="gin-tips">.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</sect2> </sect2>
</sect1> </sect1>
@ -3719,6 +3714,19 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-gin-fuzzy-search-limit" xreflabel="gin_fuzzy_search_limit">
<term><varname>gin_fuzzy_search_limit</varname> (<type>integer</type>)</term>
<indexterm>
<primary><varname>gin_fuzzy_search_limit</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Soft upper limit of the size of the set returned by GIN index. For more
information see <xref linkend="gin-tips">.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-local-preload-libraries" xreflabel="local_preload_libraries"> <varlistentry id="guc-local-preload-libraries" xreflabel="local_preload_libraries">
<term><varname>local_preload_libraries</varname> (<type>string</type>)</term> <term><varname>local_preload_libraries</varname> (<type>string</type>)</term>
<indexterm> <indexterm>

View file

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.511 2006/10/07 16:43:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.512 2006/10/07 19:25:28 tgl Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
@ -23,13 +23,20 @@
#include <signal.h> #include <signal.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/socket.h> #include <sys/socket.h>
#if HAVE_SYS_SELECT_H #ifdef HAVE_SYS_SELECT_H
#include <sys/select.h> #include <sys/select.h>
#endif #endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif
#ifndef HAVE_GETRUSAGE
#include "rusagestub.h"
#endif
#include "access/printtup.h" #include "access/printtup.h"
#include "access/xact.h" #include "access/xact.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
@ -78,7 +85,7 @@ bool Log_disconnections = false;
LogStmtLevel log_statement = LOGSTMT_NONE; LogStmtLevel log_statement = LOGSTMT_NONE;
/* GUC variable for maximum stack depth (measured in kilobytes) */ /* GUC variable for maximum stack depth (measured in kilobytes) */
int max_stack_depth = 2048; int max_stack_depth = 100;
/* wait N seconds to allow attach from a debugger */ /* wait N seconds to allow attach from a debugger */
int PostAuthDelay = 0; int PostAuthDelay = 0;
@ -91,7 +98,7 @@ int PostAuthDelay = 0;
*/ */
/* max_stack_depth converted to bytes for speed of checking */ /* max_stack_depth converted to bytes for speed of checking */
static long max_stack_depth_bytes = 2048 * 1024L; static long max_stack_depth_bytes = 100 * 1024L;
/* /*
* Stack base pointer -- initialized by PostgresMain. This is not static * Stack base pointer -- initialized by PostgresMain. This is not static
@ -2490,9 +2497,7 @@ ProcessInterrupts(void)
* This should be called someplace in any recursive routine that might possibly * This should be called someplace in any recursive routine that might possibly
* recurse deep enough to overflow the stack. Most Unixen treat stack * recurse deep enough to overflow the stack. Most Unixen treat stack
* overflow as an unrecoverable SIGSEGV, so we want to error out ourselves * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
* before hitting the hardware limit. Unfortunately we have no direct way * before hitting the hardware limit.
* to detect the hardware limit, so we have to rely on the admin to set a
* GUC variable for it ...
*/ */
void void
check_stack_depth(void) check_stack_depth(void)
@ -2530,13 +2535,24 @@ check_stack_depth(void)
} }
} }
/* GUC assign hook to update max_stack_depth_bytes from max_stack_depth */ /* GUC assign hook for max_stack_depth */
bool bool
assign_max_stack_depth(int newval, bool doit, GucSource source) assign_max_stack_depth(int newval, bool doit, GucSource source)
{ {
/* Range check was already handled by guc.c */ long newval_bytes = newval * 1024L;
long stack_rlimit = get_stack_depth_rlimit();
if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
{
ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"max_stack_depth\" must not exceed %ldkB",
(stack_rlimit - STACK_DEPTH_SLOP) / 1024L),
errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.")));
return false;
}
if (doit) if (doit)
max_stack_depth_bytes = newval * 1024L; max_stack_depth_bytes = newval_bytes;
return true; return true;
} }
@ -3635,11 +3651,36 @@ PostgresMain(int argc, char *argv[], const char *username)
return 1; /* keep compiler quiet */ return 1; /* keep compiler quiet */
} }
#ifndef HAVE_GETRUSAGE
#include "rusagestub.h" /*
#else * Obtain platform stack depth limit (in bytes)
#include <sys/resource.h> *
#endif /* HAVE_GETRUSAGE */ * Return -1 if unlimited or not known
*/
long
get_stack_depth_rlimit(void)
{
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_STACK)
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 = -1;
else
val = rlim.rlim_cur;
}
return val;
#else /* no getrlimit */
return -1;
#endif
}
static struct rusage Save_r; static struct rusage Save_r;
static struct timeval Save_t; static struct timeval Save_t;

View file

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.355 2006/10/06 17:14:00 petere Exp $ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.356 2006/10/07 19:25:28 tgl Exp $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
@ -1214,7 +1214,7 @@ static struct config_int ConfigureNamesInt[] =
GUC_UNIT_KB GUC_UNIT_KB
}, },
&max_stack_depth, &max_stack_depth,
2048, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL 100, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL
}, },
{ {
@ -1610,7 +1610,7 @@ static struct config_int ConfigureNamesInt[] =
}, },
{ {
{"gin_fuzzy_search_limit", PGC_USERSET, UNGROUPED, {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."), gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL, NULL,
0 0
@ -2702,6 +2702,7 @@ InitializeGUCOptions(void)
{ {
int i; int i;
char *env; char *env;
long stack_rlimit;
/* /*
* Build sorted array of all GUC variables. * Build sorted array of all GUC variables.
@ -2839,6 +2840,27 @@ InitializeGUCOptions(void)
env = getenv("PGCLIENTENCODING"); env = getenv("PGCLIENTENCODING");
if (env != NULL) if (env != NULL)
SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR); SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
/*
* rlimit isn't exactly an "environment variable", but it behaves about
* the same. If we can identify the platform stack depth rlimit, increase
* default stack depth setting up to whatever is safe (but at most 2MB).
*/
stack_rlimit = get_stack_depth_rlimit();
if (stack_rlimit > 0)
{
int new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L;
if (new_limit > 100)
{
char limbuf[16];
new_limit = Min(new_limit, 2048);
sprintf(limbuf, "%d", new_limit);
SetConfigOption("max_stack_depth", limbuf,
PGC_POSTMASTER, PGC_S_ENV_VAR);
}
}
} }

View file

@ -161,6 +161,9 @@
/* Define to 1 if you have the `getpwuid_r' function. */ /* Define to 1 if you have the `getpwuid_r' function. */
#undef HAVE_GETPWUID_R #undef HAVE_GETPWUID_R
/* Define to 1 if you have the `getrlimit' function. */
#undef HAVE_GETRLIMIT
/* Define to 1 if you have the `getrusage' function. */ /* Define to 1 if you have the `getrusage' function. */
#undef HAVE_GETRUSAGE #undef HAVE_GETRUSAGE
@ -460,6 +463,9 @@
/* Define to 1 if you have the <sys/pstat.h> header file. */ /* Define to 1 if you have the <sys/pstat.h> header file. */
#undef HAVE_SYS_PSTAT_H #undef HAVE_SYS_PSTAT_H
/* Define to 1 if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
/* Define to 1 if you have the <sys/select.h> header file. */ /* Define to 1 if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H #undef HAVE_SYS_SELECT_H

View file

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.84 2006/10/04 00:30:10 momjian Exp $ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.85 2006/10/07 19:25:29 tgl Exp $
* *
* OLD COMMENTS * OLD COMMENTS
* This file was created so that other c files could get the two * This file was created so that other c files could get the two
@ -23,6 +23,9 @@
#include "utils/guc.h" #include "utils/guc.h"
/* Required daylight between max_stack_depth and the kernel limit, in bytes */
#define STACK_DEPTH_SLOP (512 * 1024L)
extern CommandDest whereToSendOutput; extern CommandDest whereToSendOutput;
extern DLLIMPORT const char *debug_query_string; extern DLLIMPORT const char *debug_query_string;
extern int max_stack_depth; extern int max_stack_depth;
@ -62,6 +65,7 @@ extern void FloatExceptionHandler(SIGNAL_ARGS);
extern void prepare_for_client_read(void); extern void prepare_for_client_read(void);
extern void client_read_ended(void); extern void client_read_ended(void);
extern int PostgresMain(int argc, char *argv[], const char *username); extern int PostgresMain(int argc, char *argv[], const char *username);
extern long get_stack_depth_rlimit(void);
extern void ResetUsage(void); extern void ResetUsage(void);
extern void ShowUsage(const char *title); extern void ShowUsage(const char *title);
extern int check_log_duration(char *msec_str, bool was_logged); extern int check_log_duration(char *msec_str, bool was_logged);

View file

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/pg_rusage.h,v 1.2 2006/03/05 15:59:07 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/pg_rusage.h,v 1.3 2006/10/07 19:25:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -16,7 +16,7 @@
#include <sys/time.h> #include <sys/time.h>
#ifdef HAVE_GETRUSAGE #ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h> #include <sys/resource.h>
#else #else
#include "rusagestub.h" #include "rusagestub.h"