[PATCH v2 1/2] lib: parser: reject out-of-range values in match_number()
From: Shashank Mohan Jain
Date: Fri Sep 25 2026 - 21:27:44 EST
match_int(), match_octal() and match_hex() store the result in an int
and return -EINVAL or -ERANGE on failure. match_number() checks the
range by parsing into a long with simple_strtol() and comparing against
INT_MIN/INT_MAX, a check added by commit 77dd3b0bd17a ("lib/parser.c:
avoid overflow in match_number()"). That does not catch every
out-of-range input:
- simple_strtoull() saturates to ULLONG_MAX on overflow and
simple_strtol() simply converts its result to long, so any value of
at least 2^64 - 2^31, and anything that overflows 64 bits, ends up
inside the int range. On 64-bit, match_int() returns 0 and sets
the result to -1 for "18446744073709551615" or
"99999999999999999999", match_hex() does the same for
"ffffffffffffffff", and "-18446744073709551615" gives 1.
- On 32-bit, long has the same width as int, so the range check can
never fail: "2147483648" gives INT_MIN and "4294967295" gives -1.
On 64-bit, values from INT_MAX + 1 up to 2^64 - 2^31 - 1 are already
rejected, so for those this only makes 32-bit kernels behave like
64-bit ones. Two callers store the result in a u32 and so accepted
such values on 32-bit only: the legacy NFSv4 idmapper upcall
(fs/nfs/nfs4idmap.c), which falls back to a numeric id when the lookup
fails, and rd_pages= in drivers/target/target_core_rd.c, which also
ignores match_int()'s return value.
These helpers parse mount options and similar user-supplied strings,
so an out-of-range number is silently accepted as a different value
instead of being rejected.
Parse the number the same way simple_strtol() does, using the same
kstrtox helpers, but keep the unsigned magnitude and the overflow
indication, and check the magnitude against the int range. The
accepted syntax, including what counts as "no number" (-EINVAL), is
unchanged. kstrtoint() is not used because it rejects trailing
characters ("12abc" gives 12 today) and differs in the handling of a
lone "-" or "0x".
Fixes: 77dd3b0bd17a ("lib/parser.c: avoid overflow in match_number()")
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <jain.sm@xxxxxxxxx>
---
These patches were prepared with Claude Code (Anthropic), model Claude Opus 5.5
(claude-opus-5-5): the analysis, the Lean models used to find and check the bugs,
the fixes and the tests. I reviewed them and take responsibility for them. The
trailer only says "Assisted-by: LLM", as Documentation/process/coding-assistants.rst
requires since commit 816d9992d9ed ("coding-assistants: simplify attribution").
Changes in v2:
- Resent with my full name; no code changes.
v1: https://lore.kernel.org/r/20260925102334.49693-1-jain.sm@xxxxxxxxx
lib/parser.c | 39 +++++++++++++++++++++++++++------------
1 file changed, 27 insertions(+), 12 deletions(-)
diff --git a/lib/parser.c b/lib/parser.c
index 62da0ac0d438..30fdaefe4957 100644
--- a/lib/parser.c
+++ b/lib/parser.c
@@ -11,6 +11,8 @@
#include <linux/slab.h>
#include <linux/string.h>
+#include "kstrtox.h"
+
/*
* max size needed by different bases to express U64
* HEX: "0xFFFFFFFFFFFFFFFF" --> 18
@@ -137,22 +139,35 @@ EXPORT_SYMBOL(match_token);
*/
static int match_number(substring_t *s, int *result, int base)
{
- char *endp;
char buf[NUMBER_BUF_LEN];
- int ret;
- long val;
+ unsigned long long val;
+ unsigned int radix = base;
+ const char *cp = buf;
+ bool negative;
+ unsigned int rv;
if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
return -ERANGE;
- ret = 0;
- val = simple_strtol(buf, &endp, base);
- if (endp == buf)
- ret = -EINVAL;
- else if (val < (long)INT_MIN || val > (long)INT_MAX)
- ret = -ERANGE;
- else
- *result = (int) val;
- return ret;
+
+ /*
+ * Parse like simple_strtol() does, but keep the magnitude and the
+ * overflow indication instead of truncating to a long, so that
+ * values outside the range of an int can be rejected.
+ */
+ negative = *cp == '-';
+ if (negative)
+ cp++;
+ cp = _parse_integer_fixup_radix(cp, &radix);
+ rv = _parse_integer(cp, radix, &val);
+ if (cp + (rv & ~KSTRTOX_OVERFLOW) == buf)
+ return -EINVAL;
+
+ if (rv & KSTRTOX_OVERFLOW ||
+ val > (negative ? (unsigned long long)INT_MAX + 1 : INT_MAX))
+ return -ERANGE;
+
+ *result = negative ? -(long long)val : (long long)val;
+ return 0;
}
/**
--
2.43.0