From 0818de16f9febea3d90ae0404b4f5b3643f6cbe0 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 v2 1/2] 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        |  6 ++-
 src/bin/pg_basebackup/pg_basebackup.c  | 13 +++--
 src/bin/pg_basebackup/pg_receivewal.c  | 18 +++++--
 src/bin/pg_basebackup/pg_recvlogical.c | 17 +++++--
 src/bin/pg_checksums/pg_checksums.c    |  7 ++-
 src/bin/pg_ctl/pg_ctl.c                | 18 ++++++-
 src/bin/pg_dump/pg_dump.c              | 39 ++++++++-------
 src/bin/pg_dump/pg_restore.c           | 17 ++++---
 src/bin/pg_upgrade/option.c            | 21 ++++++--
 src/bin/pgbench/pgbench.c              | 66 ++++++++++++++++----------
 src/bin/psql/command.c                 | 52 ++++++++++++++++++--
 src/bin/scripts/reindexdb.c            | 10 ++--
 src/bin/scripts/vacuumdb.c             | 23 +++++----
 13 files changed, 219 insertions(+), 88 deletions(-)

diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 4bde16fb4b..71a82f9b75 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,8 +327,9 @@ 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 || errno == ERANGE || opts.jobs < 1)
 				{
 					pg_log_error("number of parallel jobs must be at least 1");
 					exit(1);
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 8bb0acf498..29be95b96a 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,8 +2373,10 @@ 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);
 					exit(1);
@@ -2409,8 +2413,9 @@ 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 || errno == ERANGE || standby_message_timeout < 0)
 				{
 					pg_log_error("invalid status interval \"%s\"", optarg);
 					exit(1);
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index c1334fad35..7fef925b99 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,8 @@ 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;
+
 		switch (c)
 		{
 			case 'D':
@@ -532,7 +535,9 @@ main(int argc, char **argv)
 				dbhost = pg_strdup(optarg);
 				break;
 			case 'p':
-				if (atoi(optarg) <= 0)
+				errno = 0;
+				if (strtoint(optarg, &endptr, 10) <= 0 ||
+					*endptr || errno == ERANGE)
 				{
 					pg_log_error("invalid port number \"%s\"", optarg);
 					exit(1);
@@ -549,8 +554,9 @@ 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 || errno == ERANGE || standby_message_timeout < 0)
 				{
 					pg_log_error("invalid status interval \"%s\"", optarg);
 					exit(1);
@@ -574,8 +580,10 @@ 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);
 					exit(1);
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index 76bd153fac..7be932d025 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,8 @@ 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;
+
 		switch (c)
 		{
 /* general options */
@@ -739,8 +742,9 @@ 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 || errno == ERANGE || fsync_interval < 0)
 				{
 					pg_log_error("invalid fsync interval \"%s\"", optarg);
 					exit(1);
@@ -763,7 +767,9 @@ main(int argc, char **argv)
 				dbhost = pg_strdup(optarg);
 				break;
 			case 'p':
-				if (atoi(optarg) <= 0)
+				errno = 0;
+				if (strtoint(optarg, &endptr, 10) <= 0 ||
+					*endptr || errno == ERANGE)
 				{
 					pg_log_error("invalid port number \"%s\"", optarg);
 					exit(1);
@@ -820,8 +826,9 @@ 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 || errno == ERANGE || standby_message_timeout < 0)
 				{
 					pg_log_error("invalid status interval \"%s\"", optarg);
 					exit(1);
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 3c326906e2..1c4e5b9d85 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,8 @@ main(int argc, char *argv[])
 
 	while ((c = getopt_long(argc, argv, "cD:deNPf:v", long_options, &option_index)) != -1)
 	{
+		char *endptr;
+
 		switch (c)
 		{
 			case 'c':
@@ -518,7 +521,9 @@ main(int argc, char *argv[])
 				mode = PG_MODE_ENABLE;
 				break;
 			case 'f':
-				if (atoi(optarg) == 0)
+				errno = 0;
+				if (strtoint(optarg, &endptr, 10) == 0
+					|| *endptr || errno == ERANGE)
 				{
 					pg_log_error("invalid filenode specification, must be numeric: %s", optarg);
 					exit(1);
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 7985da0a94..d6a39182cf 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -2331,6 +2331,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 +2398,13 @@ main(int argc, char **argv)
 #endif
 					break;
 				case 't':
-					wait_seconds = atoi(optarg);
+					errno = 0;
+					wait_seconds = strtoint(optarg, &endptr, 10);
+					if (*endptr || errno == ERANGE || wait_seconds < 1)
+					{
+						pg_log_error("invalid timeout value \"%s\", use --no-wait to finish without waiting", optarg);
+						exit(1);
+					}
 					wait_seconds_arg = true;
 					break;
 				case 'U':
@@ -2459,7 +2467,13 @@ 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 || errno == ERANGE || killproc < 0)
+				{
+					pg_log_error("invalid process ID \"%s\"", argv[optind]);
+					exit(1);
+				}
 			}
 #ifdef WIN32
 			else if (strcmp(argv[optind], "register") == 0)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 321152151d..793f4b3509 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"
@@ -486,7 +487,19 @@ main(int argc, char **argv)
 				break;
 
 			case 'j':			/* number of dump jobs */
-				numWorkers = atoi(optarg);
+				errno = 0;
+				numWorkers = strtoint(optarg, &endptr, 10);
+				/*
+				 * 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 (*endptr || errno == ERANGE || numWorkers <= 0
+#ifdef WIN32
+					|| numWorkers > MAXIMUM_WAIT_OBJECTS
+#endif
+					)
+					fatal("invalid number of parallel jobs %s", optarg);
 				break;
 
 			case 'n':			/* include schema(s) */
@@ -549,8 +562,10 @@ 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");
 					exit_nicely(1);
@@ -587,8 +602,10 @@ 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");
 					exit_nicely(1);
@@ -719,18 +736,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..285a09aaac 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
@@ -151,6 +152,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 +184,13 @@ main(int argc, char **argv)
 				break;
 
 			case 'j':			/* number of restore jobs */
-				numWorkers = atoi(optarg);
+				errno = 0;
+				numWorkers = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE || numWorkers <= 0)
+				{
+					pg_log_error("invalid number of parallel jobs");
+					exit(1);
+				}
 				break;
 
 			case 'l':			/* Dump the TOC summary */
@@ -344,12 +353,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)
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 64bbda5650..f96f0d1e2a 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,12 @@ parseCommandLine(int argc, char *argv[])
 				break;
 
 			case 'j':
-				user_opts.jobs = atoi(optarg);
+				errno = 0;
+				user_opts.jobs = strtoint(optarg, &endptr, 10);
+				/**/
+				if (*endptr || errno == ERANGE || user_opts.jobs < 1)
+					pg_fatal("invalid number of jobs %s\n", optarg);
+					
 				break;
 
 			case 'k':
@@ -166,13 +173,17 @@ 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;
+				if ((old_cluster.port = strtoint(optarg, &endptr, 10)) <= 0 ||
+					*endptr || errno == ERANGE)
+					pg_fatal("invalid old port number %s\n", optarg);
 				break;
 
 			case 'P':
-				if ((new_cluster.port = atoi(optarg)) <= 0)
-					pg_fatal("invalid new port number\n");
+				errno = 0;
+				if ((new_cluster.port = strtoint(optarg, &endptr, 10)) <= 0 ||
+					*endptr || errno == ERANGE)
+					pg_fatal("invalid new port number %s\n", optarg);
 				break;
 
 			case 'r':
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..4020347585 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -5838,6 +5838,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)
 		{
@@ -5869,8 +5870,9 @@ 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 || errno == ERANGE || nclients <= 0)
 				{
 					pg_log_fatal("invalid number of clients: \"%s\"", optarg);
 					exit(1);
@@ -5896,8 +5898,9 @@ 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 || errno == ERANGE || nthreads <= 0)
 				{
 					pg_log_fatal("invalid number of threads: \"%s\"", optarg);
 					exit(1);
@@ -5920,8 +5923,9 @@ 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 || errno == ERANGE || scale <= 0)
 				{
 					pg_log_fatal("invalid scaling factor: \"%s\"", optarg);
 					exit(1);
@@ -5929,8 +5933,9 @@ main(int argc, char **argv)
 				break;
 			case 't':
 				benchmarking_option_set = true;
-				nxacts = atoi(optarg);
-				if (nxacts <= 0)
+				errno = 0;
+				nxacts = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE || nxacts <= 0)
 				{
 					pg_log_fatal("invalid number of transactions: \"%s\"", optarg);
 					exit(1);
@@ -5938,8 +5943,9 @@ main(int argc, char **argv)
 				break;
 			case 'T':
 				benchmarking_option_set = true;
-				duration = atoi(optarg);
-				if (duration <= 0)
+				errno = 0;
+				duration = strtoint(optarg, &endptr, 10);
+				if (*endptr || errno == ERANGE || duration <= 0)
 				{
 					pg_log_fatal("invalid duration: \"%s\"", optarg);
 					exit(1);
@@ -6001,8 +6007,10 @@ 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);
 					exit(1);
@@ -6021,8 +6029,9 @@ 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 || errno == ERANGE || progress <= 0)
 				{
 					pg_log_fatal("invalid thread progress delay: \"%s\"", optarg);
 					exit(1);
@@ -6031,11 +6040,13 @@ main(int argc, char **argv)
 			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 || errno == ERANGE || throttle_value <= 0.0)
 					{
 						pg_log_fatal("invalid rate limit: \"%s\"", optarg);
 						exit(1);
@@ -6046,9 +6057,12 @@ 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 || errno == ERANGE || limit_ms <= 0.0)
 					{
 						pg_log_fatal("invalid latency limit: \"%s\"", optarg);
 						exit(1);
@@ -6071,8 +6085,10 @@ 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);
 					exit(1);
@@ -6080,8 +6096,9 @@ main(int argc, char **argv)
 				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 || errno == ERANGE || agg_interval <= 0)
 				{
 					pg_log_fatal("invalid number of seconds for aggregation: \"%s\"", optarg);
 					exit(1);
@@ -6117,8 +6134,9 @@ 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 || errno == ERANGE || partitions < 0)
 				{
 					pg_log_fatal("invalid number of partitions: \"%s\"", optarg);
 					exit(1);
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 543401c6d6..aaed986ae1 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1039,8 +1039,11 @@ 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 || errno == ERANGE || lineno < 1)
 				{
 					pg_log_error("invalid line number: %s", ln);
 					status = PSQL_CMD_ERROR;
@@ -4283,7 +4286,21 @@ 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 || errno == ERANGE ||
+				new_value < 0 || new_value > 65535)
+			{
+				pg_log_error("\\pset: border is invalid or out of range");
+				return false;
+			}
+
+			popt->topt.border = new_value;
+		}
 	}
 
 	/* set expanded/vertical mode */
@@ -4439,7 +4456,20 @@ 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 || errno == ERANGE || new_value < 0)
+			{
+				pg_log_error("\\pset: pager_min_lines is invalid or out of range");
+				return false;
+			}
+
+			popt->topt.pager_min_lines = new_value;
+		}
 	}
 
 	/* disable "(x rows)" footer */
@@ -4455,7 +4485,19 @@ 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 || errno == ERANGE || new_value < 0)
+			{
+				pg_log_error("\\pset: column is invalid or out of range");
+				return false;
+			}
+			popt->topt.columns = new_value;
+		}
 	}
 	else
 	{
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index fc0681538a..baa68d58d8 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,11 @@ 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 || errno == ERANGE || concurrentCons <= 0)
 				{
-					pg_log_error("number of parallel jobs must be at least 1");
+					pg_log_error("number of parallel jobs must be at least 1: %s", optarg);
 					exit(1);
 				}
 				break;
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 61974baa78..93b563998a 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,16 +195,18 @@ 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 || errno == ERANGE || concurrentCons <= 0)
 				{
 					pg_log_error("number of parallel jobs must be at least 1");
 					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 || errno == ERANGE || vacopts.parallel_workers < 0)
 				{
 					pg_log_error("parallel workers for vacuum must be greater than or equal to zero");
 					exit(1);
@@ -220,16 +225,18 @@ 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 || errno == ERANGE || vacopts.min_xid_age <= 0)
 				{
 					pg_log_error("minimum transaction ID age must be at least 1");
 					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 || errno == ERANGE || vacopts.min_mxid_age <= 0)
 				{
 					pg_log_error("minimum multixact ID age must be at least 1");
 					exit(1);
-- 
2.27.0

