From 71a6f8b85b9f748dc7f33c1212c4474e8beb901a Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 7 Oct 2006 19:25:29 +0000
Subject: [PATCH] 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().
---
 configure                     |  6 ++-
 configure.in                  |  6 +--
 contrib/pgbench/pgbench.c     | 19 +++++-----
 doc/src/sgml/config.sgml      | 42 ++++++++++++---------
 src/backend/tcop/postgres.c   | 71 +++++++++++++++++++++++++++--------
 src/backend/utils/misc/guc.c  | 28 ++++++++++++--
 src/include/pg_config.h.in    |  6 +++
 src/include/tcop/tcopprot.h   |  6 ++-
 src/include/utils/pg_rusage.h |  4 +-
 9 files changed, 136 insertions(+), 52 deletions(-)

diff --git a/configure b/configure
index f37d2f6683d..f01b03b15c8 100755
--- a/configure
+++ b/configure
@@ -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
 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
 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
 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
 echo "$as_me:$LINENO: checking for $ac_func" >&5
diff --git a/configure.in b/configure.in
index 52921466235..444ca1d8ff3 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 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 Developers, please strive to achieve this order:
 dnl
@@ -717,7 +717,7 @@ fi
 ##
 
 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
 # netinet/in.h is included first.
@@ -865,7 +865,7 @@ PGAC_VAR_INT_TIMEZONE
 AC_FUNC_ACCEPT_ARGTYPES
 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(posix_fadvise, [], [], [#include <fcntl.h>])
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 447e8487b0a..3c15346e899 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -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
  * written by Tatsuo Ishii
@@ -37,8 +37,9 @@
 #include <sys/select.h>
 #endif
 
-/* for getrlimit */
-#include <sys/resource.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>		/* for getrlimit */
+#endif
 #endif   /* ! WIN32 */
 
 extern char *optarg;
@@ -1172,7 +1173,7 @@ main(int argc, char **argv)
 	int			nsocks;			/* return from select(2) */
 	int			maxsock;		/* max socket number to be waited */
 
-#if !(defined(__CYGWIN__) || defined(__MINGW32__))
+#ifdef HAVE_GETRLIMIT
 	struct rlimit rlim;
 #endif
 
@@ -1233,8 +1234,8 @@ main(int argc, char **argv)
 					fprintf(stderr, "invalid number of clients: %d\n", nclients);
 					exit(1);
 				}
-#if !(defined(__CYGWIN__) || defined(__MINGW32__))
-#ifdef RLIMIT_NOFILE			/* most platform uses RLIMIT_NOFILE */
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_NOFILE			/* most platforms use RLIMIT_NOFILE */
 				if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
 #else							/* but BSD doesn't ... */
 				if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
@@ -1245,11 +1246,11 @@ main(int argc, char **argv)
 				}
 				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, "Use limit/ulimt to increase the limit before using pgbench.\n");
+					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/ulimit to increase the limit before using pgbench.\n");
 					exit(1);
 				}
-#endif
+#endif /* HAVE_GETRLIMIT */
 				break;
 			case 'C':
 				is_connect = 1;
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b3223e3de43..549ca755d70 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -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">
   <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
         margin is needed because the stack depth is not checked in every
         routine in the server, but only in key potentially-recursive routines
-        such as expression evaluation.  Setting the parameter higher than
-        the actual kernel limit will mean that a runaway recursive function
-        can crash an individual backend process.  The default setting is
+        such as expression evaluation.  The default setting is
         2048 KB (two megabytes), which is conservatively small and unlikely
         to risk crashes.  However, it may be too small to allow execution
         of complex functions.
         Only superusers can change this setting.
        </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>
      </varlistentry>
 
@@ -2173,19 +2181,6 @@ SELECT * FROM parent WHERE key = 2400;
       </listitem>
      </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>
     </sect2>
    </sect1>
@@ -3718,6 +3713,19 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
        </para>
       </listitem>
      </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">
       <term><varname>local_preload_libraries</varname> (<type>string</type>)</term>
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 9145904b49a..ee63220d956 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.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
  *	  this is the "main" module of the postgres backend and
@@ -23,13 +23,20 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <sys/socket.h>
-#if HAVE_SYS_SELECT_H
+#ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
 
+#ifndef HAVE_GETRUSAGE
+#include "rusagestub.h"
+#endif
+
 #include "access/printtup.h"
 #include "access/xact.h"
 #include "catalog/pg_type.h"
@@ -78,7 +85,7 @@ bool		Log_disconnections = false;
 LogStmtLevel log_statement = LOGSTMT_NONE;
 
 /* 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 */
 int			PostAuthDelay = 0;
@@ -91,7 +98,7 @@ int			PostAuthDelay = 0;
  */
 
 /* 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
@@ -2490,9 +2497,7 @@ ProcessInterrupts(void)
  * 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 ...
+ * before hitting the hardware limit.
  */
 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
 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)
-		max_stack_depth_bytes = newval * 1024L;
+		max_stack_depth_bytes = newval_bytes;
 	return true;
 }
 
@@ -3635,11 +3651,36 @@ PostgresMain(int argc, char *argv[], const char *username)
 	return 1;					/* keep compiler quiet */
 }
 
-#ifndef HAVE_GETRUSAGE
-#include "rusagestub.h"
-#else
-#include <sys/resource.h>
-#endif   /* HAVE_GETRUSAGE */
+
+/*
+ * Obtain platform stack depth limit (in bytes)
+ *
+ * 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 timeval Save_t;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 96244ec91f8..d118755a3c3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * 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
 		},
 		&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."),
 			NULL,
 			0
@@ -2702,6 +2702,7 @@ InitializeGUCOptions(void)
 {
 	int			i;
 	char	   *env;
+	long		stack_rlimit;
 
 	/*
 	 * Build sorted array of all GUC variables.
@@ -2839,6 +2840,27 @@ InitializeGUCOptions(void)
 	env = getenv("PGCLIENTENCODING");
 	if (env != NULL)
 		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);
+		}
+	}
 }
 
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c6fe8879868..0043e9afaf2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -161,6 +161,9 @@
 /* Define to 1 if you have the `getpwuid_r' function. */
 #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. */
 #undef HAVE_GETRUSAGE
 
@@ -460,6 +463,9 @@
 /* Define to 1 if you have the <sys/pstat.h> header file. */
 #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. */
 #undef HAVE_SYS_SELECT_H
 
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index e6637edeb7f..2006b7c6828 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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
  *	  This file was created so that other c files could get the two
@@ -23,6 +23,9 @@
 #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 DLLIMPORT const char *debug_query_string;
 extern int	max_stack_depth;
@@ -62,6 +65,7 @@ extern void FloatExceptionHandler(SIGNAL_ARGS);
 extern void prepare_for_client_read(void);
 extern void client_read_ended(void);
 extern int	PostgresMain(int argc, char *argv[], const char *username);
+extern long get_stack_depth_rlimit(void);
 extern void ResetUsage(void);
 extern void ShowUsage(const char *title);
 extern int	check_log_duration(char *msec_str, bool was_logged);
diff --git a/src/include/utils/pg_rusage.h b/src/include/utils/pg_rusage.h
index 32e409076e1..bb11812da80 100644
--- a/src/include/utils/pg_rusage.h
+++ b/src/include/utils/pg_rusage.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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>
 
-#ifdef HAVE_GETRUSAGE
+#ifdef HAVE_SYS_RESOURCE_H
 #include <sys/resource.h>
 #else
 #include "rusagestub.h"
-- 
GitLab