[PATCH 05/28] batctl: improve number parsing error handling

Sven Eckelmann <[email protected]> Sun, 21 Jun 2026 16:23:55 +0200
Newsgroups org.open-mesh.lists.batman
Message-ID <[email protected]>
The strtoul error handling is rather complicated and it is not only about
checking the return value. The possible error indicators are:

* endptr is NULL
* endptr is not pointing to end delimiter
* endptr is pointing at nptr (because it might have been an empty string)
* returned value is larger than the expected maximum value range

THe last two conditions were not checked even when it is a potential
problem for multiple places.

Fixes: df5c452a4469 ("batctl: Add elp_interval setting command")
Fixes: 74b6d3bd7763 ("batctl: Parse the arguments for gw_mode")
Fixes: cde0af829351 ("batctl: Add hop_penalty setting command")
Fixes: a319ec4dcbff ("batctl: Support generic netlink for isolation_mark command")
Fixes: 1ca604d5a0f2 ("batctl: add switch for setting multicast_fanout")
Fixes: c49893119205 ("batctl: Support generic netlink for orig_interval command")
Signed-off-by: Sven Eckelmann <[email protected]>
---
 elp_interval.c     | 7 +++++--
 functions.c        | 2 +-
 gw_mode.c          | 8 ++++++--
 hop_penalty.c      | 7 +++++--
 isolation_mark.c   | 8 ++++----
 multicast_fanout.c | 7 +++++--
 orig_interval.c    | 7 +++++--
 7 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/elp_interval.c b/elp_interval.c
index 7dcfa5f..71edf7f 100644
--- a/elp_interval.c
+++ b/elp_interval.c
@@ -22,6 +22,7 @@ static int parse_elp_interval(struct state *state, int argc, char *argv[])
 {
 	struct settings_data *settings = state->cmd->arg;
 	struct elp_interval_data *data = settings->data;
+	unsigned long elp_interval;
 	char *endptr;
 
 	if (argc != 2) {
@@ -29,12 +30,14 @@ static int parse_elp_interval(struct state *state, int argc, char *argv[])
 		return -EINVAL;
 	}
 
-	data->elp_interval = strtoul(argv[1], &endptr, 0);
-	if (!endptr || *endptr != '\0') {
+	elp_interval = strtoul(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || endptr == argv[1] || elp_interval > UINT32_MAX) {
 		fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
 		return -EINVAL;
 	}
 
+	data->elp_interval = elp_interval;
+
 	return 0;
 }
 
diff --git a/functions.c b/functions.c
index 5e1cb40..dc6bf75 100644
--- a/functions.c
+++ b/functions.c
@@ -970,7 +970,7 @@ bool parse_throughput(char *buff, const char *description, uint32_t *throughput)
 	}
 
 	lthroughput = strtoull(buff, &endptr, 10);
-	if (!endptr || *endptr != '\0') {
+	if (!endptr || *endptr != '\0' || endptr == buff) {
 		fprintf(stderr, "Invalid throughput speed for %s: %s\n",
 			description, buff);
 		return false;
diff --git a/gw_mode.c b/gw_mode.c
index 767a7f8..a7cc9a0 100644
--- a/gw_mode.c
+++ b/gw_mode.c
@@ -90,6 +90,7 @@ static int parse_gw_limit(char *buff)
 
 static int parse_gw(struct state *state, int argc, char *argv[])
 {
+	unsigned long sel_class;
 	char buff[256];
 	char *endptr;
 	int ret;
@@ -131,13 +132,16 @@ static int parse_gw(struct state *state, int argc, char *argv[])
 					      &gw_globals.sel_class))
 				return -EINVAL;
 		} else {
-			gw_globals.sel_class = strtoul(buff, &endptr, 0);
-			if (!endptr || *endptr != '\0') {
+			sel_class = strtoul(buff, &endptr, 0);
+			if (!endptr || *endptr != '\0' || endptr == buff ||
+			    sel_class > UINT32_MAX) {
 				fprintf(stderr,
 					"Error - unexpected argument for mode \"client\": %s\n",
 					buff);
 				return -EINVAL;
 			}
+
+			gw_globals.sel_class = sel_class;
 		}
 
 		gw_globals.sel_class_found = 1;
diff --git a/hop_penalty.c b/hop_penalty.c
index f22d36c..db727ac 100644
--- a/hop_penalty.c
+++ b/hop_penalty.c
@@ -22,6 +22,7 @@ static int parse_hop_penalty(struct state *state, int argc, char *argv[])
 {
 	struct settings_data *settings = state->cmd->arg;
 	struct hop_penalty_data *data = settings->data;
+	unsigned long hop_penalty;
 	char *endptr;
 
 	if (argc != 2) {
@@ -29,12 +30,14 @@ static int parse_hop_penalty(struct state *state, int argc, char *argv[])
 		return -EINVAL;
 	}
 
-	data->hop_penalty = strtoul(argv[1], &endptr, 0);
-	if (!endptr || *endptr != '\0') {
+	hop_penalty = strtoul(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || endptr == argv[1] || hop_penalty > UINT8_MAX) {
 		fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
 		return -EINVAL;
 	}
 
+	data->hop_penalty = hop_penalty;
+
 	return 0;
 }
 
diff --git a/isolation_mark.c b/isolation_mark.c
index cef4de0..a13e3db 100644
--- a/isolation_mark.c
+++ b/isolation_mark.c
@@ -23,10 +23,10 @@ static int parse_isolation_mark(struct state *state, int argc, char *argv[])
 {
 	struct settings_data *settings = state->cmd->arg;
 	struct isolation_mark_data *data;
+	unsigned long mark;
+	unsigned long mask;
 	char *mask_ptr;
 	char buff[256];
-	uint32_t mark;
-	uint32_t mask;
 	char *endptr;
 
 	if (argc != 2) {
@@ -50,13 +50,13 @@ static int parse_isolation_mark(struct state *state, int argc, char *argv[])
 		 * bitmask and not a prefix length
 		 */
 		mask = strtoul(mask_ptr, &endptr, 16);
-		if (!endptr || *endptr != '\0')
+		if (!endptr || *endptr != '\0' || endptr == mask_ptr || mask > UINT32_MAX)
 			goto inval_format;
 	}
 
 	/* the mark can be entered in any base */
 	mark = strtoul(buff, &endptr, 0);
-	if (!endptr || *endptr != '\0')
+	if (!endptr || *endptr != '\0' || endptr == buff || mark > UINT32_MAX)
 		goto inval_format;
 
 	data = settings->data;
diff --git a/multicast_fanout.c b/multicast_fanout.c
index 97d5e0a..6894266 100644
--- a/multicast_fanout.c
+++ b/multicast_fanout.c
@@ -22,6 +22,7 @@ static int parse_multicast_fanout(struct state *state, int argc, char *argv[])
 {
 	struct settings_data *settings = state->cmd->arg;
 	struct multicast_fanout_data *data;
+	unsigned long multicast_fanout;
 	char *endptr;
 
 	if (argc != 2) {
@@ -30,12 +31,14 @@ static int parse_multicast_fanout(struct state *state, int argc, char *argv[])
 	}
 
 	data = settings->data;
-	data->multicast_fanout = strtoul(argv[1], &endptr, 0);
-	if (!endptr || *endptr != '\0') {
+	multicast_fanout = strtoul(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || endptr == argv[1] || multicast_fanout > UINT32_MAX) {
 		fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
 		return -EINVAL;
 	}
 
+	data->multicast_fanout = multicast_fanout;
+
 	return 0;
 }
 
diff --git a/orig_interval.c b/orig_interval.c
index 970c752..e678d34 100644
--- a/orig_interval.c
+++ b/orig_interval.c
@@ -22,6 +22,7 @@ static int parse_orig_interval(struct state *state, int argc, char *argv[])
 {
 	struct settings_data *settings = state->cmd->arg;
 	struct orig_interval_data *data = settings->data;
+	unsigned long orig_interval;
 	char *endptr;
 
 	if (argc != 2) {
@@ -29,12 +30,14 @@ static int parse_orig_interval(struct state *state, int argc, char *argv[])
 		return -EINVAL;
 	}
 
-	data->orig_interval = strtoul(argv[1], &endptr, 0);
-	if (!endptr || *endptr != '\0') {
+	orig_interval = strtoul(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || endptr == argv[1] || orig_interval > UINT32_MAX) {
 		fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
 		return -EINVAL;
 	}
 
+	data->orig_interval = orig_interval;
+
 	return 0;
 }
 

-- 
2.47.3