From 56d4fda06a9003efe1172848180114e42066db98 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Thu, 8 Jul 2021 15:08:01 +0900
Subject: [PATCH v3 1/3] Be strict in numeric parameters on command line

Some numeric command line parameters are tolerant of valid values
followed by garbage like "123xyz".  Be strict to reject such invalid
values. Do the same for psql meta command parameters.
---
 src/bin/pg_amcheck/pg_amcheck.c        |  15 ++-
 src/bin/pg_basebackup/pg_basebackup.c  |  24 +++-
 src/bin/pg_basebackup/pg_receivewal.c  |  39 +++++--
 src/bin/pg_basebackup/pg_recvlogical.c |  44 +++++--
 src/bin/pg_checksums/pg_checksums.c    |  17 ++-
 src/bin/pg_ctl/pg_ctl.c                |  42 ++++++-
 src/bin/pg_dump/pg_dump.c              |  57 ++++++---
 src/bin/pg_dump/pg_restore.c           |  42 ++++---
 src/bin/pg_upgrade/option.c            |  30 ++++-
 src/bin/pgbench/pgbench.c              | 154 +++++++++++++++++++------
 src/bin/psql/command.c                 |  73 +++++++++++-
 src/bin/scripts/reindexdb.c            |  15 ++-
 src/bin/scripts/vacuumdb.c             |  59 ++++++++--
 13 files changed, 484 insertions(+), 127 deletions(-)

diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 4bde16fb4b..f40d58ac96 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_am_d.h"
 #include "catalog/pg_namespace_d.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "common/username.h"
 #include "fe_utils/cancel.h"
 #include "fe_utils/option_utils.h"
@@ -326,10 +327,18 @@ main(int argc, char *argv[])
 				append_btree_pattern(&opts.exclude, optarg, encoding);
 				break;
 			case 'j':
-				opts.jobs = atoi(optarg);
-				if (opts.jobs < 1)
+				errno = 0;
+				opts.jobs = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("number of parallel jobs must be at least 1");
+					pg_log_error("number of parallel jobs out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || opts.jobs < 1)
+				{
+					pg_log_error("number of parallel jobs must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 8bb0acf498..c30005f569 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -2287,6 +2287,8 @@ main(int argc, char **argv)
 	while ((c = getopt_long(argc, argv, "CD:F:r:RS:T:X:l:nNzZ:d:c:h:p:U:s:wWkvP",
 							long_options, &option_index)) != -1)
 	{
+		char   *endptr;
+
 		switch (c)
 		{
 			case 'C':
@@ -2371,10 +2373,12 @@ main(int argc, char **argv)
 #endif
 				break;
 			case 'Z':
-				compresslevel = atoi(optarg);
-				if (compresslevel < 0 || compresslevel > 9)
+				errno = 0;
+				compresslevel = strtoint(optarg, &endptr, 10);
+				if (*endptr ||
+					errno == ERANGE || compresslevel < 0 || compresslevel > 9)
 				{
-					pg_log_error("invalid compression level \"%s\"", optarg);
+					pg_log_error("compression level must be a digit in range 0..9: \"%s\"", optarg);
 					exit(1);
 				}
 				break;
@@ -2409,10 +2413,18 @@ main(int argc, char **argv)
 				dbgetpassword = 1;
 				break;
 			case 's':
-				standby_message_timeout = atoi(optarg) * 1000;
-				if (standby_message_timeout < 0)
+				errno = 0;
+				standby_message_timeout = strtoint(optarg, &endptr, 10) * 1000;
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid status interval \"%s\"", optarg);
+					pg_log_error("status interval out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || standby_message_timeout < 0)
+				{
+					pg_log_error("status interval must be a non-negative integer: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index c1334fad35..fb03147fe7 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -22,6 +22,7 @@
 #include "access/xlog_internal.h"
 #include "common/file_perm.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
 #include "receivelog.h"
@@ -520,6 +521,9 @@ main(int argc, char **argv)
 	while ((c = getopt_long(argc, argv, "D:d:E:h:p:U:s:S:nwWvZ:",
 							long_options, &option_index)) != -1)
 	{
+		char   *endptr;
+		int		v;
+
 		switch (c)
 		{
 			case 'D':
@@ -532,9 +536,17 @@ main(int argc, char **argv)
 				dbhost = pg_strdup(optarg);
 				break;
 			case 'p':
-				if (atoi(optarg) <= 0)
+				errno = 0;
+				v = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid port number \"%s\"", optarg);
+					pg_log_error("port number out of range: \"%s\"", optarg);
+					exit(1);
+				}
+				if (*endptr || v < 1)
+				{
+					pg_log_error("port number must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				dbport = pg_strdup(optarg);
@@ -549,10 +561,18 @@ main(int argc, char **argv)
 				dbgetpassword = 1;
 				break;
 			case 's':
-				standby_message_timeout = atoi(optarg) * 1000;
-				if (standby_message_timeout < 0)
+				errno = 0;
+				standby_message_timeout = strtoint(optarg, &endptr, 10) * 1000;
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid status interval \"%s\"", optarg);
+					pg_log_error("status interval out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || standby_message_timeout < 0)
+				{
+					pg_log_error("status interval must be a non-negative integer: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
@@ -574,10 +594,13 @@ main(int argc, char **argv)
 				verbose++;
 				break;
 			case 'Z':
-				compresslevel = atoi(optarg);
-				if (compresslevel < 0 || compresslevel > 9)
+				errno = 0;
+				compresslevel = strtoint(optarg, &endptr, 10);
+				if (*endptr ||
+					errno == ERANGE || compresslevel < 0 || compresslevel > 9)
 				{
-					pg_log_error("invalid compression level \"%s\"", optarg);
+					pg_log_error("compression level must be a digit in range 0..9: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index 76bd153fac..9bc4902033 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -23,6 +23,7 @@
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
 #include "libpq/pqsignal.h"
@@ -732,6 +733,9 @@ main(int argc, char **argv)
 	while ((c = getopt_long(argc, argv, "E:f:F:nvtd:h:p:U:wWI:o:P:s:S:",
 							long_options, &option_index)) != -1)
 	{
+		char   *endptr;
+		int		v;
+
 		switch (c)
 		{
 /* general options */
@@ -739,10 +743,18 @@ main(int argc, char **argv)
 				outfile = pg_strdup(optarg);
 				break;
 			case 'F':
-				fsync_interval = atoi(optarg) * 1000;
-				if (fsync_interval < 0)
+				errno = 0;
+				fsync_interval = strtoint(optarg, &endptr, 10) * 1000;
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid fsync interval \"%s\"", optarg);
+					pg_log_error("fsync interval out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || fsync_interval < 0)
+				{
+					pg_log_error("fsync interval must be a non-negative integer: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
@@ -763,9 +775,17 @@ main(int argc, char **argv)
 				dbhost = pg_strdup(optarg);
 				break;
 			case 'p':
-				if (atoi(optarg) <= 0)
+				errno = 0;
+				v = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid port number \"%s\"", optarg);
+					pg_log_error("port number out of range: \"%s\"", optarg);
+					exit(1);
+				}
+				if (*endptr || v < 1)
+				{
+					pg_log_error("port number must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				dbport = pg_strdup(optarg);
@@ -820,10 +840,18 @@ main(int argc, char **argv)
 				plugin = pg_strdup(optarg);
 				break;
 			case 's':
-				standby_message_timeout = atoi(optarg) * 1000;
-				if (standby_message_timeout < 0)
+				errno = 0;
+				standby_message_timeout = strtoint(optarg, &endptr, 10) * 1000;
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid status interval \"%s\"", optarg);
+					pg_log_error("status interval out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || standby_message_timeout < 0)
+				{
+					pg_log_error("status interval must be a non-negative integer: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 3c326906e2..78a1d4ef38 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -24,6 +24,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "getopt_long.h"
 #include "pg_getopt.h"
 #include "storage/bufpage.h"
@@ -506,6 +507,9 @@ main(int argc, char *argv[])
 
 	while ((c = getopt_long(argc, argv, "cD:deNPf:v", long_options, &option_index)) != -1)
 	{
+		char   *endptr;
+		int		v;
+
 		switch (c)
 		{
 			case 'c':
@@ -518,9 +522,18 @@ main(int argc, char *argv[])
 				mode = PG_MODE_ENABLE;
 				break;
 			case 'f':
-				if (atoi(optarg) == 0)
+				errno = 0;
+				v = strtoint(optarg, &endptr, 10);
+				if(*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid filenode specification, must be numeric: %s", optarg);
+					pg_log_error("filenode specification out of range: %s",
+								 optarg);
+					exit(1);
+				}
+				if(*endptr || v < 1)
+				{
+					pg_log_error("filenode specification must be an integer greater than zero: %s",
+								 optarg);
 					exit(1);
 				}
 				only_filenode = pstrdup(optarg);
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 7985da0a94..0f72ef016b 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -76,6 +76,7 @@ typedef enum
 
 #define WAITS_PER_SEC	10		/* should divide USEC_PER_SEC evenly */
 
+static bool do_wait_arg = false;
 static bool do_wait = true;
 static int	wait_seconds = DEFAULT_WAIT;
 static bool wait_seconds_arg = false;
@@ -2331,6 +2332,8 @@ main(int argc, char **argv)
 	/* process command-line options */
 	while (optind < argc)
 	{
+		char *endptr;
+
 		while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW",
 								long_options, &option_index)) != -1)
 		{
@@ -2396,7 +2399,20 @@ main(int argc, char **argv)
 #endif
 					break;
 				case 't':
-					wait_seconds = atoi(optarg);
+					errno = 0;
+					wait_seconds = strtoint(optarg, &endptr, 10);
+					if (*endptr == 0 && errno == ERANGE)
+					{
+						pg_log_error("timeout value out of range: \"%s\"",
+									 optarg);
+						exit(1);
+					}
+					if (*endptr || wait_seconds < 0)
+					{
+						pg_log_error("timeout value must be a non-negative integer: \"%s\"",
+									 optarg);
+						exit(1);
+					}
 					wait_seconds_arg = true;
 					break;
 				case 'U':
@@ -2408,6 +2424,7 @@ main(int argc, char **argv)
 					break;
 				case 'w':
 					do_wait = true;
+					do_wait_arg = true;
 					break;
 				case 'W':
 					do_wait = false;
@@ -2459,7 +2476,20 @@ main(int argc, char **argv)
 				}
 				ctl_command = KILL_COMMAND;
 				set_sig(argv[++optind]);
-				killproc = atol(argv[++optind]);
+				errno = 0;
+				killproc = strtol(argv[++optind], &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
+				{
+					pg_log_error("process ID out of range: \"%s\"",
+								 argv[optind]);
+					exit(1);
+				}
+				if (*endptr || killproc < 0)
+				{
+					pg_log_error("process ID must be a non-negative integer: \"%s\"",
+								 argv[optind]);
+					exit(1);
+				}
 			}
 #ifdef WIN32
 			else if (strcmp(argv[optind], "register") == 0)
@@ -2514,6 +2544,14 @@ main(int argc, char **argv)
 		do_wait = false;
 	}
 
+	if (wait_seconds == 0 && do_wait)
+	{
+		/* Warn if user instructed to wait but we actually don't */
+		if (!silent_mode && do_wait_arg)
+			write_stderr(_("%s: WARNING: -w is ignored because timeout is set to 0\n"), progname);
+		do_wait = false;
+	}
+
 	if (pg_data)
 	{
 		snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 321152151d..8ef29b37f6 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_trigger_d.h"
 #include "catalog/pg_type_d.h"
 #include "common/connect.h"
+#include "common/string.h"
 #include "dumputils.h"
 #include "fe_utils/string_utils.h"
 #include "getopt_long.h"
@@ -103,6 +104,17 @@ static Oid	g_last_builtin_oid; /* value of the last builtin oid */
 /* The specified names/patterns should to match at least one entity */
 static int	strict_names = 0;
 
+/*
+ * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually)
+ * parallel jobs because that's the maximum limit for the
+ * WaitForMultipleObjects() call.
+ */
+#ifndef WIN32
+#define	MAX_NUM_WORKERS INT_MAX
+#else
+#define	MAX_NUM_WORKERS MAXIMUM_WAIT_OBJECTS
+#endif
+
 /*
  * Object inclusion/exclusion lists
  *
@@ -486,7 +498,21 @@ main(int argc, char **argv)
 				break;
 
 			case 'j':			/* number of dump jobs */
-				numWorkers = atoi(optarg);
+				errno = 0;
+				numWorkers = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 &&
+					(errno == ERANGE || numWorkers > MAX_NUM_WORKERS))
+				{
+					pg_log_error("number of parallel jobs out of range: \"%s\"",
+								 optarg);
+					exit_nicely(1);
+				}
+				if (*endptr || numWorkers <= 0)
+				{
+					pg_log_error("number of parallel jobs must be an integer greater than zero: \"%s\"",
+								 optarg);
+					exit_nicely(1);
+				}
 				break;
 
 			case 'n':			/* include schema(s) */
@@ -549,10 +575,12 @@ main(int argc, char **argv)
 				break;
 
 			case 'Z':			/* Compression Level */
-				compressLevel = atoi(optarg);
-				if (compressLevel < 0 || compressLevel > 9)
+				errno = 0;
+				compressLevel = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE ||
+					compressLevel < 0 || compressLevel > 9)
 				{
-					pg_log_error("compression level must be in range 0..9");
+					pg_log_error("compression level must be a digit in range 0..9: \"%s\"", optarg);
 					exit_nicely(1);
 				}
 				break;
@@ -587,10 +615,13 @@ main(int argc, char **argv)
 
 			case 8:
 				have_extra_float_digits = true;
-				extra_float_digits = atoi(optarg);
-				if (extra_float_digits < -15 || extra_float_digits > 3)
+				errno = 0;
+				extra_float_digits = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE ||
+					extra_float_digits < -15 || extra_float_digits > 3)
 				{
-					pg_log_error("extra_float_digits must be in range -15..3");
+					pg_log_error("extra_float_digits must be an integer in range -15..3: \"%s\"",
+						optarg);
 					exit_nicely(1);
 				}
 				break;
@@ -719,18 +750,6 @@ main(int argc, char **argv)
 	if (!plainText)
 		dopt.outputCreateDB = 1;
 
-	/*
-	 * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually)
-	 * parallel jobs because that's the maximum limit for the
-	 * WaitForMultipleObjects() call.
-	 */
-	if (numWorkers <= 0
-#ifdef WIN32
-		|| numWorkers > MAXIMUM_WAIT_OBJECTS
-#endif
-		)
-		fatal("invalid number of parallel jobs");
-
 	/* Parallel backup only in the directory archive format so far */
 	if (archiveFormat != archDirectory && numWorkers > 1)
 		fatal("parallel backup only supported by the directory format");
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 589b4aed53..3bb5a48c55 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -39,6 +39,7 @@
  *-------------------------------------------------------------------------
  */
 #include "postgres_fe.h"
+#include "common/string.h"
 
 #include <ctype.h>
 #ifdef HAVE_TERMIOS_H
@@ -52,6 +53,13 @@
 
 static void usage(const char *progname);
 
+/* See comments in pg_dump.c */
+#ifndef WIN32
+#define MAX_NUM_WORKERS INT_MAX
+#else
+#define MAX_NUM_WORKERS MAXIMUM_WAIT_OBJECTS
+#endif
+
 int
 main(int argc, char **argv)
 {
@@ -151,6 +159,8 @@ main(int argc, char **argv)
 	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1",
 							cmdopts, NULL)) != -1)
 	{
+		char *endptr;
+
 		switch (c)
 		{
 			case 'a':			/* Dump data only */
@@ -181,7 +191,21 @@ main(int argc, char **argv)
 				break;
 
 			case 'j':			/* number of restore jobs */
-				numWorkers = atoi(optarg);
+				errno = 0;
+				numWorkers = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 &&
+					(errno == ERANGE || numWorkers > MAX_NUM_WORKERS))
+				{
+					pg_log_error("number of parallel jobs out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || numWorkers <= 0)
+				{
+					pg_log_error("number of parallel jobs must be an integer greater than zero: \"%s\"",
+								 optarg);
+					exit(1);
+				}
 				break;
 
 			case 'l':			/* Dump the TOC summary */
@@ -344,22 +368,6 @@ main(int argc, char **argv)
 		exit_nicely(1);
 	}
 
-	if (numWorkers <= 0)
-	{
-		pg_log_error("invalid number of parallel jobs");
-		exit(1);
-	}
-
-	/* See comments in pg_dump.c */
-#ifdef WIN32
-	if (numWorkers > MAXIMUM_WAIT_OBJECTS)
-	{
-		pg_log_error("maximum number of parallel jobs is %d",
-					 MAXIMUM_WAIT_OBJECTS);
-		exit(1);
-	}
-#endif
-
 	/* Can't do single-txn mode with multiple connections */
 	if (opts->single_txn && numWorkers > 1)
 	{
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 64bbda5650..c014bbca0d 100644
--- a/src/bin/pg_upgrade/option.c
+++ b/src/bin/pg_upgrade/option.c
@@ -104,6 +104,8 @@ parseCommandLine(int argc, char *argv[])
 	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
 								 long_options, &optindex)) != -1)
 	{
+		char   *endptr;
+
 		switch (option)
 		{
 			case 'b':
@@ -127,7 +129,15 @@ parseCommandLine(int argc, char *argv[])
 				break;
 
 			case 'j':
-				user_opts.jobs = atoi(optarg);
+				errno = 0;
+				user_opts.jobs = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
+					pg_fatal("number of parallel jobs out of range: \"%s\"\n",
+							 optarg);
+				if (*endptr || user_opts.jobs < 1)
+					pg_fatal("number of parallel jobs must be an integer greater than zero: \"%s\"\n",
+							 optarg);
+					
 				break;
 
 			case 'k':
@@ -166,13 +176,23 @@ parseCommandLine(int argc, char *argv[])
 				 * supported on all old/new versions (added in PG 9.2).
 				 */
 			case 'p':
-				if ((old_cluster.port = atoi(optarg)) <= 0)
-					pg_fatal("invalid old port number\n");
+				errno = 0;
+				old_cluster.port = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
+					pg_fatal("old port number out of range: \"%s\"\n", optarg);
+				if (*endptr || old_cluster.port <= 0)
+					pg_fatal("old port number must be an integer greater than zero: \"%s\"\n",
+							 optarg);
 				break;
 
 			case 'P':
-				if ((new_cluster.port = atoi(optarg)) <= 0)
-					pg_fatal("invalid new port number\n");
+				errno = 0;
+				new_cluster.port = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
+					pg_fatal("new port number out of range: \"%s\"\n", optarg);
+				if (*endptr || new_cluster.port <= 0)
+					pg_fatal("new port number must be an integer greater than zero: \"%s\"\n",
+							 optarg);
 				break;
 
 			case 'r':
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 364b5a2e47..1c3b5836c1 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -5856,6 +5856,7 @@ main(int argc, char **argv)
 	while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) != -1)
 	{
 		char	   *script;
+		char 	   *endptr;
 
 		switch (c)
 		{
@@ -5887,10 +5888,18 @@ main(int argc, char **argv)
 				break;
 			case 'c':
 				benchmarking_option_set = true;
-				nclients = atoi(optarg);
-				if (nclients <= 0)
+				errno = 0;
+				nclients = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid number of clients: \"%s\"", optarg);
+					pg_log_fatal("number of clients out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || nclients <= 0)
+				{
+					pg_log_fatal("number of clients must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 #ifdef HAVE_GETRLIMIT
@@ -5914,10 +5923,18 @@ main(int argc, char **argv)
 				break;
 			case 'j':			/* jobs */
 				benchmarking_option_set = true;
-				nthreads = atoi(optarg);
-				if (nthreads <= 0)
+				errno = 0;
+				nthreads = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid number of threads: \"%s\"", optarg);
+					pg_log_fatal("number of threads out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || nthreads <= 0)
+				{
+					pg_log_fatal("number of threads must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 #ifndef ENABLE_THREAD_SAFETY
@@ -5938,28 +5955,50 @@ main(int argc, char **argv)
 				break;
 			case 's':
 				scale_given = true;
-				scale = atoi(optarg);
-				if (scale <= 0)
+				errno = 0;
+				scale = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid scaling factor: \"%s\"", optarg);
+					pg_log_fatal("scaling factor out of range: \"%s\"", optarg);
+					exit(1);
+				}
+				if (*endptr || scale <= 0)
+				{
+					pg_log_fatal("scaling factor must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
 			case 't':
 				benchmarking_option_set = true;
-				nxacts = atoi(optarg);
-				if (nxacts <= 0)
+				errno = 0;
+				nxacts = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid number of transactions: \"%s\"", optarg);
+					pg_log_fatal("number of transactions out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || nxacts <= 0)
+				{
+					pg_log_fatal("number of transactions must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
 			case 'T':
 				benchmarking_option_set = true;
-				duration = atoi(optarg);
-				if (duration <= 0)
+				errno = 0;
+				duration = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid duration: \"%s\"", optarg);
+					pg_log_fatal("duration out of range: \"%s\"", optarg);
+					exit(1);
+				}
+				if (*endptr || duration <= 0)
+				{
+					pg_log_fatal("duration must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
@@ -6019,10 +6058,13 @@ main(int argc, char **argv)
 				break;
 			case 'F':
 				initialization_option_set = true;
-				fillfactor = atoi(optarg);
-				if (fillfactor < 10 || fillfactor > 100)
+				errno = 0;
+				fillfactor = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE ||
+					fillfactor < 10 || fillfactor > 100)
 				{
-					pg_log_fatal("invalid fillfactor: \"%s\"", optarg);
+					pg_log_fatal("fillfactor must be an ineger between 10 and 100: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
@@ -6039,23 +6081,38 @@ main(int argc, char **argv)
 				break;
 			case 'P':
 				benchmarking_option_set = true;
-				progress = atoi(optarg);
-				if (progress <= 0)
+				errno = 0;
+				progress = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid thread progress delay: \"%s\"", optarg);
+					pg_log_fatal("thread progress delay out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || progress <= 0)
+				{
+					pg_log_fatal("thread progress delay must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
 			case 'R':
 				{
 					/* get a double from the beginning of option value */
-					double		throttle_value = atof(optarg);
+					double		throttle_value;
 
+					errno = 0;
+					throttle_value = strtod(optarg, &endptr);
 					benchmarking_option_set = true;
 
-					if (throttle_value <= 0.0)
+					if (*endptr == 0 && errno == ERANGE)
 					{
-						pg_log_fatal("invalid rate limit: \"%s\"", optarg);
+						pg_log_fatal("rate limit out of range: \"%s\"", optarg);
+						exit(1);
+					}
+					if (*endptr || throttle_value <= 0.0)
+					{
+						pg_log_fatal("rate limit must be a real number greater than zero: \"%s\"", optarg);
 						exit(1);
 					}
 					/* Invert rate limit into per-transaction delay in usec */
@@ -6064,11 +6121,20 @@ main(int argc, char **argv)
 				break;
 			case 'L':
 				{
-					double		limit_ms = atof(optarg);
+					double		limit_ms;
 
-					if (limit_ms <= 0.0)
+					errno = 0;
+					limit_ms = strtod(optarg, &endptr);
+
+					if (*endptr == 0 && errno == ERANGE)
+					{
+						pg_log_fatal("latency limit out of range: \"%s\"",
+									 optarg);
+						exit(1);
+					}
+					if (*endptr || limit_ms <= 0.0)
 					{
-						pg_log_fatal("invalid latency limit: \"%s\"", optarg);
+						pg_log_fatal("latency limit must be a real number greater than zero: \"%s\"", optarg);
 						exit(1);
 					}
 					benchmarking_option_set = true;
@@ -6089,19 +6155,27 @@ main(int argc, char **argv)
 				break;
 			case 4:				/* sampling-rate */
 				benchmarking_option_set = true;
-				sample_rate = atof(optarg);
-				if (sample_rate <= 0.0 || sample_rate > 1.0)
+				errno = 0;
+				sample_rate = strtod(optarg, &endptr);
+				if (*endptr || errno == ERANGE ||
+					sample_rate <= 0.0 || sample_rate > 1.0)
 				{
-					pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
+					pg_log_fatal("sampling rate must be an real number between 0.0 and 1.0: \"%s\"", optarg);
 					exit(1);
 				}
 				break;
 			case 5:				/* aggregate-interval */
 				benchmarking_option_set = true;
-				agg_interval = atoi(optarg);
-				if (agg_interval <= 0)
+				errno = 0;
+				agg_interval = strtod(optarg, &endptr);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid number of seconds for aggregation: \"%s\"", optarg);
+					pg_log_fatal("aggregate interval out of range: \"%s\"", optarg);
+					exit(1);
+				}
+				if (*endptr || agg_interval <= 0)
+				{
+					pg_log_fatal("aggregate interval must be a real number greater than zero: \"%s\"", optarg);
 					exit(1);
 				}
 				break;
@@ -6135,10 +6209,18 @@ main(int argc, char **argv)
 				break;
 			case 11:			/* partitions */
 				initialization_option_set = true;
-				partitions = atoi(optarg);
-				if (partitions < 0)
+				errno = 0;
+				partitions = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_fatal("invalid number of partitions: \"%s\"", optarg);
+					pg_log_fatal("number of partitions out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || partitions < 0)
+				{
+					pg_log_fatal("number of partitions must be a non-negative integer: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index d704c4220c..13074051b3 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1040,10 +1040,18 @@ exec_command_edit(PsqlScanState scan_state, bool active_branch,
 			}
 			if (ln)
 			{
-				lineno = atoi(ln);
-				if (lineno < 1)
+				char *endptr;
+
+				errno = 0;
+				lineno = strtoint(ln, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("invalid line number: %s", ln);
+					pg_log_error("line number out of range: %s", ln);
+					status = PSQL_CMD_ERROR;
+				}
+				if (*endptr || lineno < 1)
+				{
+					pg_log_error("line number must be an integer greater than zero: %s", ln);
 					status = PSQL_CMD_ERROR;
 				}
 			}
@@ -4284,7 +4292,25 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "border") == 0)
 	{
 		if (value)
-			popt->topt.border = atoi(value);
+		{
+			char *endptr;
+			int new_value;
+
+			errno = 0;
+			new_value = strtoint(value, &endptr, 10);
+			if (*endptr == 0 && (errno == ERANGE || new_value > 65535))
+			{
+				pg_log_error("\\pset: border out of range");
+				return false;
+			}
+			if (*endptr || new_value < 0)
+			{
+				pg_log_error("\\pset: border must be an integer greater than zero");
+				return false;
+			}
+
+			popt->topt.border = new_value;
+		}
 	}
 
 	/* set expanded/vertical mode */
@@ -4440,7 +4466,25 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "pager_min_lines") == 0)
 	{
 		if (value)
-			popt->topt.pager_min_lines = atoi(value);
+		{
+			char *endptr;
+			int new_value;
+
+			errno = 0;
+			new_value = strtoint(value, &endptr, 10);
+			if (*endptr == 0 && errno == ERANGE)
+			{
+				pg_log_error("\\pset: pager_min_lines out of range");
+				return false;
+			}
+			if (*endptr || new_value < 0)
+			{
+				pg_log_error("\\pset: pager_min_lines must be a non-negative integer");
+				return false;
+			}
+
+			popt->topt.pager_min_lines = new_value;
+		}
 	}
 
 	/* disable "(x rows)" footer */
@@ -4456,7 +4500,24 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 	else if (strcmp(param, "columns") == 0)
 	{
 		if (value)
-			popt->topt.columns = atoi(value);
+		{
+			char *endptr;
+			int new_value;
+
+			errno = 0;
+			new_value = strtoint(value, &endptr, 10);
+			if (*endptr == 0 && errno == ERANGE)
+			{
+				pg_log_error("\\pset: column out of range");
+				return false;
+			}
+			if (*endptr || new_value < 0)
+			{
+				pg_log_error("\\pset: column must be a non-negative integer");
+				return false;
+			}
+			popt->topt.columns = new_value;
+		}
 	}
 	else
 	{
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index fc0681538a..42d4d20768 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "common/connect.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/cancel.h"
 #include "fe_utils/option_utils.h"
 #include "fe_utils/parallel_slot.h"
@@ -109,6 +110,8 @@ main(int argc, char *argv[])
 	/* process command-line options */
 	while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:j:v", long_options, &optindex)) != -1)
 	{
+		char *endptr;
+
 		switch (c)
 		{
 			case 'h':
@@ -151,10 +154,16 @@ main(int argc, char *argv[])
 				simple_string_list_append(&indexes, optarg);
 				break;
 			case 'j':
-				concurrentCons = atoi(optarg);
-				if (concurrentCons <= 0)
+				errno = 0;
+				concurrentCons = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("number of parallel jobs must be at least 1");
+					pg_log_error("number of parallel jobs out of range: %s", optarg);
+					exit(1);
+				}
+				if (*endptr || concurrentCons <= 0)
+				{
+					pg_log_error("number of parallel jobs must be an integer greater than zero: %s", optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 61974baa78..6b2a34edd0 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -17,6 +17,7 @@
 #include "common.h"
 #include "common/connect.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/cancel.h"
 #include "fe_utils/option_utils.h"
 #include "fe_utils/parallel_slot.h"
@@ -141,6 +142,8 @@ main(int argc, char *argv[])
 
 	while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:zZFat:fvj:P:", long_options, &optindex)) != -1)
 	{
+		char *endptr;
+
 		switch (c)
 		{
 			case 'h':
@@ -192,18 +195,34 @@ main(int argc, char *argv[])
 				vacopts.verbose = true;
 				break;
 			case 'j':
-				concurrentCons = atoi(optarg);
-				if (concurrentCons <= 0)
+				errno = 0;
+				concurrentCons = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("number of parallel jobs must be at least 1");
+					pg_log_error("number of parallel jobs out of range: \"%s\"",
+						optarg);
+					exit(1);
+				}
+				if (*endptr || concurrentCons <= 0)
+				{
+					pg_log_error("number of parallel jobs must be an integer greater than zero: \"%s\"",
+								 optarg);
 					exit(1);
 				}
 				break;
 			case 'P':
-				vacopts.parallel_workers = atoi(optarg);
-				if (vacopts.parallel_workers < 0)
+				errno = 0;
+				vacopts.parallel_workers = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("parallel workers for vacuum must be greater than or equal to zero");
+					pg_log_error("parallel workers out of range: \"%s\"",
+								 optarg);
+					exit(1);
+				}
+				if (*endptr || vacopts.parallel_workers < 0)
+				{
+					pg_log_error("parallel workers for vacuum must be a non-negative integer: \"%s\"",
+						optarg);
 					exit(1);
 				}
 				break;
@@ -220,18 +239,34 @@ main(int argc, char *argv[])
 				vacopts.skip_locked = true;
 				break;
 			case 6:
-				vacopts.min_xid_age = atoi(optarg);
-				if (vacopts.min_xid_age <= 0)
+				errno = 0;
+				vacopts.min_xid_age = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("minimum transaction ID age must be at least 1");
+					pg_log_error("minimum transaction ID age out of range: \"%s\"",
+						optarg);
+					exit(1);
+				}
+				if (*endptr || vacopts.min_xid_age <= 0)
+				{
+					pg_log_error("minimum transaction ID age must be an integer greater than zero: \"%s\"",
+						optarg);
 					exit(1);
 				}
 				break;
 			case 7:
-				vacopts.min_mxid_age = atoi(optarg);
-				if (vacopts.min_mxid_age <= 0)
+				errno = 0;
+				vacopts.min_mxid_age = strtoint(optarg, &endptr, 10);
+				if (*endptr == 0 && errno == ERANGE)
 				{
-					pg_log_error("minimum multixact ID age must be at least 1");
+					pg_log_error("minimum multixact ID age out of range: \"%s\"",
+						optarg);
+					exit(1);
+				}
+				if (*endptr || vacopts.min_mxid_age <= 0)
+				{
+					pg_log_error("minimum multixact ID age must be an integer greater than zero: \"%s\"",
+						optarg);
 					exit(1);
 				}
 				break;
-- 
2.27.0

