[PATCH 2/4] kconfig: check for out-of-bounds numeric constants

Julian Braha <[email protected]>
Newsgroups gmane.linux.kbuild.devel,gmane.linux.kernel
Message-ID <[email protected]>
The Kconfig interpreter internally represents constants as strings, then
attempts to parse them as 64-bit signed integers for 'int' options, and
64-bit unsigned integers for 'hex' options.

However, there is currently no check that the conversion succeeds, leading
to failures when the values are actually used. For example:

  config LARGE_INT
    int
    default 10000000000000000000

  config BUGGED_INT_COMPARISON
    bool
    default y if LARGE_INT < 2

Obviously 10000000000000000000 is larger than 2, but the Kconfig
interpreter will fallback to comparing the two values with strcmp() after
the numeric conversion fails, causing the first character, '1', to be
compared with '2', and giving the wrong result.

Since none of these out-of-bounds values are used as constants anywhere in
the tree, we can already make these error out.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Julian Braha <[email protected]>
---
 scripts/kconfig/menu.c                        | 60 ++++++++++----
 scripts/kconfig/tests/err_num_bounds/Kconfig  | 79 +++++++++++++++++++
 .../kconfig/tests/err_num_bounds/__init__.py  | 12 +++
 .../tests/err_num_bounds/expected_stderr      | 10 +++
 .../err_num_non_numeric_ref/expected_stderr   | 30 +++----
 5 files changed, 160 insertions(+), 31 deletions(-)
 create mode 100644 scripts/kconfig/tests/err_num_bounds/Kconfig
 create mode 100644 scripts/kconfig/tests/err_num_bounds/__init__.py
 create mode 100644 scripts/kconfig/tests/err_num_bounds/expected_stderr

diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 99a57ce0fdc9..ede791a2fe1b 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -4,6 +4,7 @@
  */
 
 #include <ctype.h>
+#include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -234,10 +235,46 @@ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
 	menu_add_prop(type, expr_alloc_symbol(sym), dep);
 }
 
-static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
+/* Validate the sym2 value for numeric sym. */
+static int menu_validate_number(struct symbol *sym, struct symbol *sym2,
+				const struct property *prop)
 {
-	return sym2->type == S_INT || sym2->type == S_HEX ||
-	       (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+	const char *type_bounds;
+
+	if (sym->type != S_INT && sym->type != S_HEX)
+		return 0;
+
+	if (sym2->type == S_INT || sym2->type == S_HEX)
+		return 0;
+
+	if (sym2->type != S_UNKNOWN ||
+		!sym_string_valid(sym, sym2->name)) {
+		fprintf(stderr, "%s:%d: error: '%s' is an invalid value for '%s'\n",
+			prop->filename, prop->lineno, sym2->name,
+			sym_type_name(sym->type));
+		return 1;
+	}
+
+	errno = 0;
+	if (sym->type == S_INT) {
+		type_bounds = "64-bit signed integer";
+		strtoll(sym2->name, NULL, 10);
+	} else {
+		/* hex */
+		type_bounds = "64-bit unsigned integer";
+		strtoull(sym2->name, NULL, 16);
+	}
+
+	if (errno == ERANGE) {
+		fprintf(stderr,
+			"%s:%d: error: %s constant '%s' is outside the %s bounds\n",
+			prop->filename, prop->lineno, sym_type_name(sym->type),
+			sym2->name, type_bounds);
+
+		return 1;
+	}
+
+	return 0;
 }
 
 static int sym_check_prop(struct symbol *sym)
@@ -259,13 +296,7 @@ static int sym_check_prop(struct symbol *sym)
 				break;
 			sym2 = prop_get_symbol(prop);
 			if (sym->type == S_HEX || sym->type == S_INT) {
-				if (!menu_validate_number(sym, sym2)) {
-					fprintf(stderr,
-						"%s:%d: error: '%s': number is invalid\n",
-						prop->filename, prop->lineno,
-						sym->name);
-					errors++;
-				}
+				errors += menu_validate_number(sym, sym2, prop);
 			}
 			if (sym_is_choice(sym)) {
 				struct menu *choice = sym_get_choice_menu(sym2);
@@ -296,13 +327,8 @@ static int sym_check_prop(struct symbol *sym)
 			if (sym->type != S_INT && sym->type != S_HEX)
 				prop_warn(prop, "range is only allowed "
 						"for int or hex symbols");
-			if (!menu_validate_number(sym, prop->expr->left.sym) ||
-			    !menu_validate_number(sym, prop->expr->right.sym)) {
-				fprintf(stderr,
-					"%s:%d: error: range is invalid\n",
-					prop->filename, prop->lineno);
-				errors++;
-			}
+			errors += menu_validate_number(sym, prop->expr->left.sym, prop);
+			errors += menu_validate_number(sym, prop->expr->right.sym, prop);
 			break;
 		default:
 			;
diff --git a/scripts/kconfig/tests/err_num_bounds/Kconfig b/scripts/kconfig/tests/err_num_bounds/Kconfig
new file mode 100644
index 000000000000..c439366c03b6
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/Kconfig
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test bounds checks for 'int' and 'hex' constants
+
+config INT_SOURCE
+	int
+
+config HEX_SOURCE
+	hex
+
+config BOOL_SOURCE
+	bool
+
+# Valid values at the limits of the type
+
+config INT_MIN
+	int
+	default -9223372036854775808
+
+config INT_MAX
+	int
+	default 9223372036854775807
+
+config HEX_MIN
+	hex
+	default 0x0
+
+config HEX_MAX
+	hex
+	default 0xffffffffffffffff
+
+config INT_RANGE_LIMITS
+	int
+	range -9223372036854775808 9223372036854775807
+
+config HEX_RANGE_LIMITS
+	hex
+	range 0 0xffffffffffffffff
+
+config INT_FROM_INT
+	int
+	default INT_SOURCE
+
+config HEX_FROM_HEX
+	hex
+	default HEX_SOURCE
+
+# Constants outside the bounds
+
+config INT_DEFAULT_TOO_HIGH
+	int
+	default 10000000000000000000
+
+config INT_DEFAULT_TOO_LOW
+	int
+	default -9223372036854775809
+
+config INT_RANGE_TOO_HIGH
+	int
+	range 0 10000000000000000000
+
+config INT_RANGE_TOO_LOW
+	int
+	range -10000000000000000000 0
+
+config INT_RANGE_BOTH_OUTSIDE
+	int
+	range -9223372036854775809 10000000000000000000
+
+config HEX_DEFAULT_TOO_HIGH
+	hex
+	default 0x10000000000000000
+
+config HEX_RANGE_TOO_HIGH
+	hex
+	range 0 0x10000000000000000
+
+config HEX_RANGE_BOTH_TOO_HIGH
+	hex
+	range 0x10000000000000000 0x20000000000000000
diff --git a/scripts/kconfig/tests/err_num_bounds/__init__.py b/scripts/kconfig/tests/err_num_bounds/__init__.py
new file mode 100644
index 000000000000..72ac6aa24491
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/__init__.py
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Detect constants outside the 'int' and 'hex' bounds.
+
+An int constant must fit in a signed 64-bit integer, and a hex constant must
+fit in an unsigned 64-bit integer.
+"""
+
+
+def test(conf):
+    assert conf.olddefconfig() == 1
+    assert conf.stderr_matches('expected_stderr')
diff --git a/scripts/kconfig/tests/err_num_bounds/expected_stderr b/scripts/kconfig/tests/err_num_bounds/expected_stderr
new file mode 100644
index 000000000000..3f06e13359ef
--- /dev/null
+++ b/scripts/kconfig/tests/err_num_bounds/expected_stderr
@@ -0,0 +1,10 @@
+Kconfig:51: error: integer constant '10000000000000000000' is outside the 64-bit signed integer bounds
+Kconfig:55: error: integer constant '-9223372036854775809' is outside the 64-bit signed integer bounds
+Kconfig:59: error: integer constant '10000000000000000000' is outside the 64-bit signed integer bounds
+Kconfig:63: error: integer constant '-10000000000000000000' is outside the 64-bit signed integer bounds
+Kconfig:67: error: integer constant '-9223372036854775809' is outside the 64-bit signed integer bounds
+Kconfig:67: error: integer constant '10000000000000000000' is outside the 64-bit signed integer bounds
+Kconfig:71: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned integer bounds
+Kconfig:75: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned integer bounds
+Kconfig:79: error: hex constant '0x10000000000000000' is outside the 64-bit unsigned integer bounds
+Kconfig:79: error: hex constant '0x20000000000000000' is outside the 64-bit unsigned integer bounds
diff --git a/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr b/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr
index 4974ba2fcd9c..005f855ecbdd 100644
--- a/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr
+++ b/scripts/kconfig/tests/err_num_non_numeric_ref/expected_stderr
@@ -1,14 +1,16 @@
-Kconfig:17: error: 'INT_DEFAULT_BOOL': number is invalid
-Kconfig:21: error: 'INT_DEFAULT_TRISTATE': number is invalid
-Kconfig:25: error: 'INT_DEFAULT_STRING': number is invalid
-Kconfig:31: error: 'HEX_DEFAULT_BOOL': number is invalid
-Kconfig:35: error: 'HEX_DEFAULT_TRISTATE': number is invalid
-Kconfig:39: error: 'HEX_DEFAULT_STRING': number is invalid
-Kconfig:45: error: range is invalid
-Kconfig:49: error: range is invalid
-Kconfig:53: error: range is invalid
-Kconfig:57: error: range is invalid
-Kconfig:63: error: range is invalid
-Kconfig:67: error: range is invalid
-Kconfig:71: error: range is invalid
-Kconfig:75: error: range is invalid
+Kconfig:17: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:21: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:25: error: 'STRING_SOURCE' is an invalid value for 'integer'
+Kconfig:31: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:35: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
+Kconfig:39: error: 'STRING_SOURCE' is an invalid value for 'hex'
+Kconfig:45: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:49: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:53: error: 'STRING_SOURCE' is an invalid value for 'integer'
+Kconfig:57: error: 'BOOL_SOURCE' is an invalid value for 'integer'
+Kconfig:57: error: 'TRISTATE_SOURCE' is an invalid value for 'integer'
+Kconfig:63: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:67: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
+Kconfig:71: error: 'STRING_SOURCE' is an invalid value for 'hex'
+Kconfig:75: error: 'BOOL_SOURCE' is an invalid value for 'hex'
+Kconfig:75: error: 'TRISTATE_SOURCE' is an invalid value for 'hex'
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.