public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH 06/12] parse: Add helper to parse unsigned integer values
Date: Fri, 26 Jun 2026 17:09:57 +1000	[thread overview]
Message-ID: <20260626071003.3472194-7-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20260626071003.3472194-1-david@gibson.dropbear.id.au>

Most places we need to parse an integer encoded as a string, we use
strtol() or strtoul().  These already work a bit like descent parser
helpers, in that they consume as much as they can, but don't require the
number parsed to be the whole of the einput string.  Make a wrapper to
parse unsigned integers as part of our parsing helper.  This wrapper
handles the mildly fiddly error checking requirements for strtoul().

We replace a number, though not all, of our existing strtoul() uses with
the new parse_unsigned().  We also replace a number of strtol() use cases,
because, despite using that instead of strtoul() they are only used for
non-negative values.  In some cases this makes the logic a little more
straightforward.  In some other cases it will catch some error cases we
previously could have missed.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile   |  1 -
 conf.c     | 51 +++++++++++++++++++++++++--------------------------
 fwd_rule.c | 42 ++++++++++++++----------------------------
 parse.c    | 27 ++++++++++++++++++++++-----
 parse.h    |  1 +
 5 files changed, 62 insertions(+), 60 deletions(-)

diff --git a/Makefile b/Makefile
index bd729c75..85d279c0 100644
--- a/Makefile
+++ b/Makefile
@@ -225,7 +225,6 @@ pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:bitmap.c
 pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.h
 pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:inany.c
 pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:ip.h
-pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:parse.c
 pesto.cppcheck: CPPCHECK_FLAGS += --suppress=unusedFunction:serialise.c
 pesto.cppcheck: CPPCHECK_FLAGS += --suppress=staticFunction:fwd_rule.c
 pesto.cppcheck: $(PESTO_SRCS) $(PESTO_HEADERS) seccomp_pesto.h
diff --git a/conf.c b/conf.c
index ea299e18..bd357117 100644
--- a/conf.c
+++ b/conf.c
@@ -321,17 +321,16 @@ static void conf_pasta_ns(int *netns_only, char *userns, char *netns,
 		die("Both --netns and PID or command given");
 
 	if (optind + 1 == argc) {
-		char *endptr;
-		long pidval;
+		const char *p = argv[optind];
+		unsigned long pidval;
 
-		pidval = strtol(argv[optind], &endptr, 10);
-		if (!*endptr) {
+		if (parse_unsigned(&p, 10, &pidval) && parse_eoi(p)) {
 			/* Looks like a pid */
-			if (pidval < 0 || pidval > INT_MAX)
+			if (pidval > INT_MAX)
 				die("Invalid PID %s", argv[optind]);
 
 			if (snprintf_check(netns, PATH_MAX,
-					   "/proc/%ld/ns/net", pidval))
+					   "/proc/%lu/ns/net", pidval))
 				die_perror("Can't build netns path");
 
 			if (!*userns) {
@@ -354,6 +353,7 @@ static void conf_pasta_ns(int *netns_only, char *userns, char *netns,
  */
 static uint8_t conf_ip4_prefix(const char *arg)
 {
+	const char *p = arg;
 	struct in_addr mask;
 	unsigned long len;
 
@@ -362,11 +362,9 @@ static uint8_t conf_ip4_prefix(const char *arg)
 		len = __builtin_popcount(hmask);
 		if ((hmask << len) == 0)
 			return len;
-	} else {
-		errno = 0;
-		len = strtoul(arg, NULL, 0);
-		if (!errno && len <= 32)
-			return len;
+	} else if (parse_unsigned(&p, 0, &len) && parse_eoi(p) &&
+		   len <= 32) {
+		return len;
 	}
 
 	die("Invalid prefix length: %s", arg);
@@ -1159,13 +1157,12 @@ static void conf_sock_listen(const struct ctx *c)
  */
 int conf_tap_fd(const char *arg)
 {
-	long val;
-
-	errno = 0;
-	val = strtol(arg, NULL, 0);
+	const char *p = arg;
+	unsigned long val;
 
-	if (errno || (val != STDIN_FILENO && val <= STDERR_FILENO) ||
-	    val > INT_MAX)
+	if (!parse_unsigned(&p, 0, &val) || !parse_eoi(p)	||
+	    val > INT_MAX					||
+	    (val != STDIN_FILENO && val <= STDERR_FILENO))
 		die("Invalid --fd: %s", arg);
 
 	return val;
@@ -1288,6 +1285,8 @@ void conf(struct ctx *c, int argc, char **argv)
 
 	optind = 0;
 	do {
+		const char *p;
+
 		name = getopt_long(argc, argv, optstring, options, NULL);
 
 		switch (name) {
@@ -1368,14 +1367,17 @@ void conf(struct ctx *c, int argc, char **argv)
 		case 12:
 			runas = optarg;
 			break;
-		case 13:
-			errno = 0;
-			logsize = strtol(optarg, NULL, 0);
+		case 13: {
+			unsigned long val;
 
-			if (logsize < LOGFILE_SIZE_MIN || errno)
+			p = optarg;
+			if (!parse_unsigned(&p, 0, &val) || !parse_eoi(p) ||
+			    val < LOGFILE_SIZE_MIN)
 				die("Invalid --log-size: %s", optarg);
 
+			logsize = val;
 			break;
+		}
 		case 14:
 			FPRINTF(stdout,
 				c->mode == MODE_PASTA ? "pasta " : "passt ");
@@ -1552,12 +1554,9 @@ void conf(struct ctx *c, int argc, char **argv)
 			break;
 		case 'm': {
 			unsigned long mtu;
-			char *e;
-
-			errno = 0;
-			mtu = strtoul(optarg, &e, 0);
 
-			if (errno || *e)
+			p = optarg;
+			if (!parse_unsigned(&p, 0, &mtu) || !parse_eoi(p))
 				die("Invalid MTU: %s", optarg);
 
 			if (mtu > max_mtu) {
diff --git a/fwd_rule.c b/fwd_rule.c
index d0e90402..d4b422d7 100644
--- a/fwd_rule.c
+++ b/fwd_rule.c
@@ -41,10 +41,11 @@ static in_port_t fwd_ephemeral_max = NUM_PORTS - 1;
  */
 void fwd_probe_ephemeral(void)
 {
-	char *line, *tab, *end;
+	unsigned long min, max;
 	struct lineread lr;
-	long min, max;
+	const char *p;
 	ssize_t len;
+	char *line;
 	int fd;
 
 	fd = open(PORT_RANGE_SYSCTL, O_RDONLY | O_CLOEXEC);
@@ -57,35 +58,20 @@ void fwd_probe_ephemeral(void)
 	len = lineread_get(&lr, &line);
 	close(fd);
 
-	if (len < 0)
-		goto parse_err;
-
-	tab = strchr(line, '\t');
-	if (!tab)
-		goto parse_err;
-	*tab = '\0';
-
-	errno = 0;
-	min = strtol(line, &end, 10);
-	if (*end || errno)
-		goto parse_err;
-
-	errno = 0;
-	max = strtol(tab + 1, &end, 10);
-	if (*end || errno)
-		goto parse_err;
-
-	if (min < 0 || min >= (long)NUM_PORTS ||
-	    max < 0 || max >= (long)NUM_PORTS)
-		goto parse_err;
+	p = line;
+	if (len < 0				||
+	    !parse_unsigned(&p, 10, &min)	||
+	    !parse_literal(&p, "\t")		||
+	    !parse_unsigned(&p, 10, &max)	||
+	    !parse_eoi(p)			||
+	    min >= NUM_PORTS			||
+	    max >= NUM_PORTS) {
+		warn("Unable to parse %s", PORT_RANGE_SYSCTL);
+		return;
+	}
 
 	fwd_ephemeral_min = min;
 	fwd_ephemeral_max = max;
-
-	return;
-
-parse_err:
-	warn("Unable to parse %s", PORT_RANGE_SYSCTL);
 }
 
 /**
diff --git a/parse.c b/parse.c
index c2a92422..8abfd4dd 100644
--- a/parse.c
+++ b/parse.c
@@ -12,14 +12,12 @@
  * Author: David Gibson <david@gibson.dropbear.id.au>
  */
 
-#include <assert.h>
+#include <errno.h>
 #include <stddef.h>
-#include <fcntl.h>
+#include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
-#include <unistd.h>
 
-#include "common.h"
+#include "parse.h"
 
 /**
  * DOC: Theory of Operation
@@ -65,3 +63,22 @@ bool parse_eoi(const char *cursor)
 {
 	return !(*cursor);
 }
+
+/*
+ * parse_unsigned() - Parse an unsigned integer
+ * @base:	Numeric base for string as strtoul(3)
+ * @valp:	On success, updated with parsed value
+ */
+bool parse_unsigned(const char **cursor, int base, unsigned long *valp)
+{
+	const char *p = *cursor;
+	unsigned long val;
+
+	errno = 0;
+	val = strtoul(p, (char **)&p, base);
+	if (errno || p == *cursor)
+		return false;
+	*valp = val;
+	*cursor = p;
+	return true;
+}
diff --git a/parse.h b/parse.h
index eb51a469..4f38c28b 100644
--- a/parse.h
+++ b/parse.h
@@ -10,5 +10,6 @@
 
 bool parse_literal(const char **cursor, const char *lit);
 bool parse_eoi(const char *cursor);
+bool parse_unsigned(const char **cursor, int base, unsigned long *valp);
 
 #endif /* _PARSE_H */
-- 
2.54.0


  parent reply	other threads:[~2026-06-26  7:10 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26  7:09 [PATCH 00/12] Rework option parsing in preparation for destination remapping David Gibson
2026-06-26  7:09 ` [PATCH 01/12] Makefile: Add missing PESTO_HEADERS variable David Gibson
2026-06-26  7:09 ` [PATCH 02/12] conf: Use parameter instead of global in conf_nat() David Gibson
2026-06-26  7:09 ` [PATCH 03/12] parse: Start splitting out parsing helpers David Gibson
2026-06-26  7:09 ` [PATCH 04/12] conf: Remove duplicate parsing of -F option David Gibson
2026-06-26  7:09 ` [PATCH 05/12] conf: Clean up conf_ip4_prefix() David Gibson
2026-06-26  7:09 ` David Gibson [this message]
2026-06-26  7:09 ` [PATCH 07/12] parse: Move parse_port_range() to new parsing framework David Gibson
2026-06-26  7:09 ` [PATCH 08/12] parse: Add helpers for parsing IP addresses David Gibson
2026-06-26  7:10 ` [PATCH 09/12] conf: Move address configuration into helper function David Gibson
2026-06-26  7:10 ` [PATCH 10/12] conf: Use new parsing tools to handle -a option David Gibson
2026-06-26  7:10 ` [PATCH 11/12] fwd_rule: Allow "all" port specs to be combined with other options David Gibson
2026-06-26  7:10 ` [PATCH 12/12] fwd_rule: Rewrite forward rule parsing using parse.c helpers David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260626071003.3472194-7-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).