* [PATCH] kconfig: reject malformed KCONFIG_PROBABILITY values
@ 2026-09-07 19:45 Dmitrii Tulnov
2026-09-08 19:24 ` Julian Braha
0 siblings, 1 reply; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-07 19:45 UTC (permalink / raw)
To: Nathan Chancellor, Nicolas Schier
Cc: Julian Braha, Peter Korsgaard, Yann E. MORIN, linux-kbuild, linux-kernel
randconfig checks the numeric range of each probability but does not
validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
accepted as 50:0:0: the '%' is parsed twice as zero. This silently sets
both tristate y/m probabilities to zero, reducing the coverage of random
configuration builds. Empty fields and extra fields are also accepted.
Require each field to contain an integer followed by the end of the
string or a colon introducing another field, with at most three fields.
Preserve the leading whitespace and optional sign accepted by strtol().
Keep the strtol() result as long until the range check so that narrowing
to int cannot turn an out-of-range value into a valid probability.
Add regression tests for malformed and out-of-range values, the supported
probability formats, and the documented empty-value default.
Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
Assisted-by: LLM
Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
---
Validation on kbuild-next, x86_64, GCC 13.3.0:
- make testconfig with HOSTCFLAGS=-Werror: 58 passed. With the original
conf binary: 18 failed, 40 passed; all failures are new regression tests.
- ASan/UBSan at -O1, with leak detection disabled: the same 58 tests passed.
- Both builds passed 1,635 input cases and 440 comparisons with the original
conf using valid inputs and fixed seeds. Malformed inputs preserved an
existing .config, including when KCONFIG_ALLCONFIG was set.
- make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
KCONFIG_PROBABILITY=50% changed from success to the expected error.
- No vmlinux build or boot test; this changes the host configuration tool.
A 32-bit host build was unavailable because multilib headers were missing.
An AI coding assistant helped find the issue, prepare the fix, description
and tests, and run validation. The requested task was to find and fix a
useful, reproducible Linux bug suitable for a first contribution.
scripts/kconfig/conf.c | 9 +++-
.../tests/randconfig_probability/Kconfig | 12 +++++
.../tests/randconfig_probability/__init__.py | 54 +++++++++++++++++++
3 files changed, 74 insertions(+), 1 deletion(-)
create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index fe8ba09b0..fa5dae74e 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -191,7 +191,14 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
n = 0;
while (env && *env) {
char *endp;
- int tmp = strtol(env, &endp, 10);
+ long tmp = strtol(env, &endp, 10);
+
+ if (endp == env || (*endp && *endp != ':') ||
+ (*endp == ':' && (!endp[1] || n == 2))) {
+ errno = EINVAL;
+ perror("KCONFIG_PROBABILITY");
+ exit(1);
+ }
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
new file mode 100644
index 000000000..84f4e5fcc
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MODULES
+ bool
+ default y
+ modules
+
+config BOOL
+ bool "Bool"
+
+config TRI
+ tristate "Tristate"
diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
new file mode 100644
index 000000000..022c7eaf2
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0-only
+"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
+
+import pytest
+
+
+@pytest.mark.parametrize('probability', [
+ 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
+ '10:20:invalid', '10:20:30x',
+ ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
+ '-1', '101', '0:101', '0:0:101', '60:41', '0:60:41',
+ '4294967296', '-4294967296',
+ '999999999999999999999999', '-999999999999999999999999',
+])
+def test_invalid(conf, monkeypatch, probability):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
+
+ assert conf.randconfig(seed=0) == 1
+ assert 'KCONFIG_PROBABILITY:' in conf.stderr
+
+
+@pytest.mark.parametrize('probability, boolean, tristate', [
+ ('0', 'n', 'n'),
+ ('0:0', 'n', 'n'),
+ ('100:0', 'y', 'y'),
+ ('0:100', 'y', 'm'),
+ ('100:0:0', 'y', 'n'),
+ ('0:100:0', 'n', 'y'),
+ ('0:0:100', 'n', 'm'),
+ ('000:000:100', 'n', 'm'),
+ ('+100:0', 'y', 'y'),
+ (' \t100:0', 'y', 'y'),
+ ('0: \t100:0', 'n', 'y'),
+])
+def test_valid(conf, monkeypatch, probability, boolean, tristate):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
+
+ assert conf.randconfig(seed=0) == 0
+ for symbol, value in [('BOOL', boolean), ('TRI', tristate)]:
+ if value == 'n':
+ expected = '# CONFIG_{} is not set'.format(symbol)
+ else:
+ expected = 'CONFIG_{}={}'.format(symbol, value)
+ assert expected in conf.config.splitlines()
+
+
+def test_empty(conf, monkeypatch):
+ monkeypatch.delenv('KCONFIG_PROBABILITY', raising=False)
+ assert conf.randconfig(seed=0) == 0
+ default_config = conf.config
+
+ monkeypatch.setenv('KCONFIG_PROBABILITY', '')
+ assert conf.randconfig(seed=0) == 0
+ assert conf.config == default_config
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] kconfig: reject malformed KCONFIG_PROBABILITY values
2026-09-07 19:45 [PATCH] kconfig: reject malformed KCONFIG_PROBABILITY values Dmitrii Tulnov
@ 2026-09-08 19:24 ` Julian Braha
2026-09-08 20:35 ` Dmitrii Tulnov
2026-09-08 20:36 ` [PATCH v2] kconfig: warn about " Dmitrii Tulnov
0 siblings, 2 replies; 10+ messages in thread
From: Julian Braha @ 2026-09-08 19:24 UTC (permalink / raw)
To: Dmitrii Tulnov, Nathan Chancellor, Nicolas Schier
Cc: Peter Korsgaard, Yann E. MORIN, linux-kbuild, linux-kernel
Hi Dmitrii,
I see that this is your first patch submission. Welcome!
And thank you for reporting this.
On 9/7/26 20:45, Dmitrii Tulnov wrote:
> randconfig checks the numeric range of each probability but does not
> validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
> accepted as 50:0:0: the '%' is parsed twice as zero. This silently sets
> both tristate y/m probabilities to zero, reducing the coverage of random
> configuration builds.
It would be good to note here that 'KCONFIG_PROBABILITY=50' (without the
'%') should be 25:25:50 instead.
> Empty fields and extra fields are also accepted.
Right, in these cases, I think it's reasonable to assume the user made a
typo or other oversight.
>
> Require each field to contain an integer followed by the end of the
> string or a colon introducing another field, with at most three fields.
> Preserve the leading whitespace and optional sign accepted by strtol().
I disagree with allowing the leading whitespace and sign. The fact that
it's allowed input for strtol() is incidental to us. The intended input
format for Kconfig is:
XX:XX:XX
not:
XX: +XX: -XX
> Keep the strtol() result as long until the range check so that narrowing
> to int cannot turn an out-of-range value into a valid probability.
>
> Add regression tests for malformed and out-of-range values, the supported
> probability formats, and the documented empty-value default.
>
> Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
> Assisted-by: LLM
> Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
> ---
> Validation on kbuild-next, x86_64, GCC 13.3.0:
> - make testconfig with HOSTCFLAGS=-Werror: 58 passed. With the original
> conf binary: 18 failed, 40 passed; all failures are new regression tests.
> - ASan/UBSan at -O1, with leak detection disabled: the same 58 tests passed.
> - Both builds passed 1,635 input cases and 440 comparisons with the original
> conf using valid inputs and fixed seeds. Malformed inputs preserved an
> existing .config, including when KCONFIG_ALLCONFIG was set.
> - make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
> KCONFIG_PROBABILITY=50% changed from success to the expected error.
> - No vmlinux build or boot test; this changes the host configuration tool.
> A 32-bit host build was unavailable because multilib headers were missing.
>
> An AI coding assistant helped find the issue, prepare the fix, description
> and tests, and run validation. The requested task was to find and fix a
> useful, reproducible Linux bug suitable for a first contribution.
>
> scripts/kconfig/conf.c | 9 +++-
> .../tests/randconfig_probability/Kconfig | 12 +++++
> .../tests/randconfig_probability/__init__.py | 54 +++++++++++++++++++
> 3 files changed, 74 insertions(+), 1 deletion(-)
> create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
> create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
>
> diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
> index fe8ba09b0..fa5dae74e 100644
> --- a/scripts/kconfig/conf.c
> +++ b/scripts/kconfig/conf.c
> @@ -191,7 +191,14 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
> n = 0;
> while (env && *env) {
> char *endp;
> - int tmp = strtol(env, &endp, 10);
> + long tmp = strtol(env, &endp, 10);
> +
> + if (endp == env || (*endp && *endp != ':') ||
> + (*endp == ':' && (!endp[1] || n == 2))) {
> + errno = EINVAL;
> + perror("KCONFIG_PROBABILITY");
> + exit(1);
Unfortunately, sometimes users depend on unintended behavior. So I would
prefer to make this a warning for now, and then promote this to an error
later assuming nobody complains.
- Julian Braha
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] kconfig: reject malformed KCONFIG_PROBABILITY values
2026-09-08 19:24 ` Julian Braha
@ 2026-09-08 20:35 ` Dmitrii Tulnov
2026-09-08 20:36 ` [PATCH v2] kconfig: warn about " Dmitrii Tulnov
1 sibling, 0 replies; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-08 20:35 UTC (permalink / raw)
To: Julian Braha
Cc: Nathan Chancellor, Nicolas Schier, Peter Korsgaard,
Yann E. MORIN, linux-kbuild, linux-kernel
Hi Julian,
Thank you for the review and for the welcome.
> It would be good to note here that 'KCONFIG_PROBABILITY=50' (without
> the '%') should be 25:25:50 instead.
You're right. The v2 commit message now states explicitly that
KCONFIG_PROBABILITY=50 gives tristate y/m/n probabilities of 25%/25%/50%.
The regression test compares the equivalent inputs 50 and 50:25:25 across
20 fixed seeds.
> I disagree with allowing the leading whitespace and sign.
Agreed. Leading whitespace and signs are now warning-only because they are
accepted by strtol() but outside Kconfig's documented XX:XX:XX format.
> I would prefer to make this a warning for now, and then promote this to an
> error later assuming nobody complains.
The v2 implementation warns about malformed syntax while retaining the
existing parsing behavior for compatibility. Numeric range violations remain
errors.
Thank you again for taking the time to review this.
Best regards,
Dmitrii
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-08 19:24 ` Julian Braha
2026-09-08 20:35 ` Dmitrii Tulnov
@ 2026-09-08 20:36 ` Dmitrii Tulnov
2026-09-10 0:05 ` Julian Braha
1 sibling, 1 reply; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-08 20:36 UTC (permalink / raw)
To: Nathan Chancellor, Nicolas Schier
Cc: Julian Braha, Peter Korsgaard, Yann E. MORIN, linux-kbuild, linux-kernel
randconfig checks the numeric range of each probability but does not
validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
accepted as 50:0:0: the '%' is parsed repeatedly as zero. This silently
sets both tristate y/m probabilities to zero, reducing the coverage of
random configuration builds. Empty fields and extra fields are also
accepted.
Warn when the value does not follow the documented decimal format. Keep
the existing parsing behavior for now so that users depending on these
inputs are not broken; the warning can be promoted to an error later.
The documented one-field value 50 gives tristate y/m/n probabilities of
25%/25%/50%. Leading whitespace and signs are accepted by strtol(), but are
also warned about because they are outside the documented format.
Keep the strtol() result as long until the range check so that narrowing
to int cannot turn an out-of-range value into a valid probability.
Add regression tests for malformed warnings and out-of-range values, the
supported probability formats, and the documented empty-value default.
Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
Assisted-by: LLM
Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
---
Changes since v1:
- Warn about malformed values while retaining their previous parsing behavior.
- Treat leading whitespace and signs as warning-only undocumented input.
- Clarify that 50 gives tristate y/m/n probabilities of 25%/25%/50%, and
compare it with the equivalent input 50:25:25 across 20 fixed seeds.
Validation on kbuild-next, x86_64, GCC 13.3.0:
- make testconfig with HOSTCFLAGS=-Werror: 78 passed. Malformed values now
produce warnings while preserving the previous parsing behavior; numeric
range errors remain fatal.
- ASan/UBSan at -O1, with leak detection disabled: the same 78 tests passed.
- The fixed build accepted 916 of 1,635 generated inputs (including
warning-only malformed values) and rejected 719 numeric range errors.
It matched the original on 380 documented-input comparisons with fixed
seeds. Range errors preserved an existing .config, including with
KCONFIG_ALLCONFIG set.
- make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
KCONFIG_PROBABILITY=50% now produces a warning and remains compatible.
- No vmlinux build or boot test; this changes the host configuration tool.
A 32-bit host build was unavailable because multilib headers were missing.
scripts/kconfig/conf.c | 14 +++-
.../tests/randconfig_probability/Kconfig | 12 +++
.../tests/randconfig_probability/__init__.py | 79 +++++++++++++++++++
3 files changed, 104 insertions(+), 1 deletion(-)
create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index fe8ba09b0..33e8baf8d 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -186,12 +186,24 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
if (mode == def_random) {
int n, p[3];
+ bool warned = false;
char *env = getenv("KCONFIG_PROBABILITY");
n = 0;
while (env && *env) {
char *endp;
- int tmp = strtol(env, &endp, 10);
+ long tmp = strtol(env, &endp, 10);
+
+ if (endp == env || isspace((unsigned char)*env) ||
+ *env == '+' || *env == '-' ||
+ (*endp && *endp != ':') ||
+ (*endp == ':' && (!endp[1] || n == 2))) {
+ if (!warned) {
+ fprintf(stderr,
+ "warning: KCONFIG_PROBABILITY has malformed format\n");
+ warned = true;
+ }
+ }
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
new file mode 100644
index 000000000..84f4e5fcc
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MODULES
+ bool
+ default y
+ modules
+
+config BOOL
+ bool "Bool"
+
+config TRI
+ tristate "Tristate"
diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
new file mode 100644
index 000000000..ff1770608
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0-only
+"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
+
+import pytest
+
+
+@pytest.mark.parametrize('probability', [
+ 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
+ '10:20:invalid', '10:20:30x',
+ ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
+ '+100:0', ' \t100:0', '0: \t100:0',
+])
+def test_malformed_warns(conf, monkeypatch, probability):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
+
+ assert conf.randconfig(seed=0) == 0
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.config is not None
+
+
+@pytest.mark.parametrize('probability', [
+ '-1', '101', '0:101', '0:0:101', '60:41', '0:60:41',
+ '4294967296', '-4294967296',
+ '999999999999999999999999', '-999999999999999999999999',
+])
+def test_out_of_range(conf, monkeypatch, probability):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
+
+ assert conf.randconfig(seed=0) == 1
+ assert 'KCONFIG_PROBABILITY:' in conf.stderr
+
+
+@pytest.mark.parametrize('probability, boolean, tristate', [
+ ('0', 'n', 'n'),
+ ('0:0', 'n', 'n'),
+ ('100:0', 'y', 'y'),
+ ('0:100', 'y', 'm'),
+ ('100:0:0', 'y', 'n'),
+ ('0:100:0', 'n', 'y'),
+ ('0:0:100', 'n', 'm'),
+ ('000:000:100', 'n', 'm'),
+])
+def test_valid(conf, monkeypatch, probability, boolean, tristate):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
+
+ assert conf.randconfig(seed=0) == 0
+ assert 'warning:' not in conf.stderr
+ for symbol, value in [('BOOL', boolean), ('TRI', tristate)]:
+ if value == 'n':
+ expected = '# CONFIG_{} is not set'.format(symbol)
+ else:
+ expected = 'CONFIG_{}={}'.format(symbol, value)
+ assert expected in conf.config.splitlines()
+
+
+@pytest.mark.parametrize('seed', range(20))
+def test_single_probability_matches_tristate_split(conf, monkeypatch, seed):
+ monkeypatch.setenv('KCONFIG_PROBABILITY', '50')
+ assert conf.randconfig(seed=seed) == 0
+ assert 'warning:' not in conf.stderr
+ single = conf.config
+
+ # 50% boolean y; 25% tristate y, 25% m, and 50% n.
+ monkeypatch.setenv('KCONFIG_PROBABILITY', '50:25:25')
+ assert conf.randconfig(seed=seed) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == single
+
+
+def test_empty(conf, monkeypatch):
+ monkeypatch.delenv('KCONFIG_PROBABILITY', raising=False)
+ assert conf.randconfig(seed=0) == 0
+ assert 'warning:' not in conf.stderr
+ default_config = conf.config
+
+ monkeypatch.setenv('KCONFIG_PROBABILITY', '')
+ assert conf.randconfig(seed=0) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == default_config
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-08 20:36 ` [PATCH v2] kconfig: warn about " Dmitrii Tulnov
@ 2026-09-10 0:05 ` Julian Braha
2026-09-10 9:13 ` Dmitrii Tulnov
0 siblings, 1 reply; 10+ messages in thread
From: Julian Braha @ 2026-09-10 0:05 UTC (permalink / raw)
To: Dmitrii Tulnov, Nathan Chancellor, Nicolas Schier
Cc: Peter Korsgaard, Yann E. MORIN, linux-kbuild, linux-kernel
Hi Dmitrii,
On 9/8/26 21:36, Dmitrii Tulnov wrote:
> randconfig checks the numeric range of each probability but does not
> validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
> accepted as 50:0:0: the '%' is parsed repeatedly as zero. This silently
> sets both tristate y/m probabilities to zero, reducing the coverage of
> random configuration builds. Empty fields and extra fields are also
> accepted.
>
> Warn when the value does not follow the documented decimal format. Keep
> the existing parsing behavior for now so that users depending on these
> inputs are not broken; the warning can be promoted to an error later.
> The documented one-field value 50 gives tristate y/m/n probabilities of
> 25%/25%/50%. Leading whitespace and signs are accepted by strtol(), but are
This "25:25:50" explanation belongs in the first paragraph where you
give the background and state that "KCONFIG_PROBABILITY=50% is accepted
as 50:0:0". Your second paragraph is about what you're changing.
> also warned about because they are outside the documented format.
> Keep the strtol() result as long until the range check so that narrowing
> to int cannot turn an out-of-range value into a valid probability.
>
> Add regression tests for malformed warnings and out-of-range values, the
> supported probability formats, and the documented empty-value default.
>
> Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
> Assisted-by: LLM
> Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
> ---
> Changes since v1:
> - Warn about malformed values while retaining their previous parsing behavior.
> - Treat leading whitespace and signs as warning-only undocumented input.
> - Clarify that 50 gives tristate y/m/n probabilities of 25%/25%/50%, and
> compare it with the equivalent input 50:25:25 across 20 fixed seeds.
>
> Validation on kbuild-next, x86_64, GCC 13.3.0:
> - make testconfig with HOSTCFLAGS=-Werror: 78 passed. Malformed values now
> produce warnings while preserving the previous parsing behavior; numeric
> range errors remain fatal.
> - ASan/UBSan at -O1, with leak detection disabled: the same 78 tests passed.
> - The fixed build accepted 916 of 1,635 generated inputs (including
> warning-only malformed values) and rejected 719 numeric range errors.
> It matched the original on 380 documented-input comparisons with fixed
> seeds. Range errors preserved an existing .config, including with
> KCONFIG_ALLCONFIG set.
> - make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
> KCONFIG_PROBABILITY=50% now produces a warning and remains compatible.
> - No vmlinux build or boot test; this changes the host configuration tool.
> A 32-bit host build was unavailable because multilib headers were missing.
>
> scripts/kconfig/conf.c | 14 +++-
> .../tests/randconfig_probability/Kconfig | 12 +++
> .../tests/randconfig_probability/__init__.py | 79 +++++++++++++++++++
> 3 files changed, 104 insertions(+), 1 deletion(-)
> create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
> create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
>
> diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
> index fe8ba09b0..33e8baf8d 100644
> --- a/scripts/kconfig/conf.c
> +++ b/scripts/kconfig/conf.c
> @@ -186,12 +186,24 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
>
> if (mode == def_random) {
> int n, p[3];
> + bool warned = false;
> char *env = getenv("KCONFIG_PROBABILITY");
>
> n = 0;
> while (env && *env) {
> char *endp;
> - int tmp = strtol(env, &endp, 10);
> + long tmp = strtol(env, &endp, 10);
> +
> + if (endp == env || isspace((unsigned char)*env) ||
> + *env == '+' || *env == '-' ||
This condition expression is pretty long. You can simplify this:
endp == env || isspace((unsigned char)*env) ||
*env == '+' || *env == '-'
into:
!isdigit((unsigned char)*env)
> + (*endp && *endp != ':') ||
> + (*endp == ':' && (!endp[1] || n == 2))) {
> + if (!warned) {
> + fprintf(stderr,
> + "warning: KCONFIG_PROBABILITY has malformed format\n");
> + warned = true;
> + }
> + }
>
> if (tmp >= 0 && tmp <= 100) {
> p[n++] = tmp;
> diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
> new file mode 100644
> index 000000000..84f4e5fcc
> --- /dev/null
> +++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config MODULES
> + bool
> + default y
> + modules
> +
> +config BOOL
> + bool "Bool"
> +
> +config TRI
> + tristate "Tristate"
> diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
> new file mode 100644
> index 000000000..ff1770608
> --- /dev/null
> +++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
> @@ -0,0 +1,79 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
> +
> +import pytest
> +
> +
> +@pytest.mark.parametrize('probability', [
> + 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
> + '10:20:invalid', '10:20:30x',
> + ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
> + '+100:0', ' \t100:0', '0: \t100:0',
> +])
> +def test_malformed_warns(conf, monkeypatch, probability):
> + monkeypatch.setenv('KCONFIG_PROBABILITY', probability)
You should set the environment variables using 'conf._run_conf' instead
of 'monkeypatch.setenv'.
For an example, see the test in:
scripts/kconfig/tests/warn_changed_input/
and how it uses 'extra_env'.
> +
> + assert conf.randconfig(seed=0) == 0
> + assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
> + assert conf.config is not None
> +
> +
> +@pytest.mark.parametrize('probability', [
> + '-1', '101', '0:101', '0:0:101', '60:41', '0:60:41',
> + '4294967296', '-4294967296',
> + '999999999999999999999999', '-999999999999999999999999',
It would be good to include '-0', which is the only negative input
accepted before this patch.
Also you should include an instance with '+'.
- Julian Braha
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-10 0:05 ` Julian Braha
@ 2026-09-10 9:13 ` Dmitrii Tulnov
2026-09-10 11:25 ` Julian Braha
0 siblings, 1 reply; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-10 9:13 UTC (permalink / raw)
To: julianbraha
Cc: nathan, nsc, jacmet, yann.morin.1998, linux-kbuild, linux-kernel
Hi Julian,
Thanks for the review. I've addressed your comments locally for v3.
Before sending it, I'd like to clarify how the new warning should
interact with KCONFIG_WERROR.
With KCONFIG_PROBABILITY=50% and KCONFIG_WERROR=1, v2 and my current
v3 still exit successfully and write .config. The warning is printed
directly and does not go through the existing warning/error handling.
Would you prefer it to become an error when KCONFIG_WERROR is set,
while remaining a warning otherwise, or to stay non-fatal even in
that mode during the compatibility period? I'd lean toward honoring
the explicit strict mode.
For the compatibility tests, I propose comparing the generated
configurations for 50% versus 50:0:0 and -0 versus 0 with the same
seed, and checking that the malformed input produces exactly one
warning. That would put checks for the preserved interpretation in
the submitted tests, in addition to the local comparisons I've run.
Does that sound appropriate for this patch?
Best regards,
Dmitrii
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-10 9:13 ` Dmitrii Tulnov
@ 2026-09-10 11:25 ` Julian Braha
2026-09-10 12:04 ` [PATCH v3] " Dmitrii Tulnov
0 siblings, 1 reply; 10+ messages in thread
From: Julian Braha @ 2026-09-10 11:25 UTC (permalink / raw)
To: Dmitrii Tulnov
Cc: nathan, nsc, jacmet, yann.morin.1998, linux-kbuild, linux-kernel
On 9/10/26 10:13, Dmitrii Tulnov wrote:
> Hi Julian,
>
> Thanks for the review. I've addressed your comments locally for v3.
> Before sending it, I'd like to clarify how the new warning should
> interact with KCONFIG_WERROR.
>
> With KCONFIG_PROBABILITY=50% and KCONFIG_WERROR=1, v2 and my current
> v3 still exit successfully and write .config. The warning is printed
> directly and does not go through the existing warning/error handling.
>
> Would you prefer it to become an error when KCONFIG_WERROR is set,
> while remaining a warning otherwise, or to stay non-fatal even in
> that mode during the compatibility period? I'd lean toward honoring
> the explicit strict mode.
Yes, better to support it. Thanks!
>
> For the compatibility tests, I propose comparing the generated
> configurations for 50% versus 50:0:0 and -0 versus 0 with the same
> seed, and checking that the malformed input produces exactly one
> warning. That would put checks for the preserved interpretation in
> the submitted tests, in addition to the local comparisons I've run.
> Does that sound appropriate for this patch?
>
Yeah that's fine.
> Best regards,
> Dmitrii
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-10 11:25 ` Julian Braha
@ 2026-09-10 12:04 ` Dmitrii Tulnov
2026-09-10 21:17 ` Julian Braha
0 siblings, 1 reply; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-10 12:04 UTC (permalink / raw)
To: nathan, nsc
Cc: julianbraha, jacmet, yann.morin.1998, linux-kbuild, linux-kernel
randconfig checks the numeric range of each probability but does not
validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
accepted as 50:0:0: the '%' is parsed repeatedly as zero. For the
documented value 50, tristate y/m/n probabilities are 25%/25%/50%.
With 50%, both y/m probabilities silently become zero, reducing the
coverage of random configuration builds. Empty fields and extra fields
are also accepted.
Warn when the value does not follow the documented decimal format. For
malformed inputs whose parsed probabilities are in range, preserve the
existing interpretation unless KCONFIG_WERROR is set. In that case, exit
with an error before writing the configuration. Leading whitespace and
signs are accepted by strtol(), but also trigger the warning because they
are outside the documented format.
Keep the strtol() result as long until the range check. This rejects
out-of-range values that narrowing to int previously made valid, such as
4294967296 becoming zero on a 64-bit host, regardless of KCONFIG_WERROR.
Add regression tests for one warning per malformed input, preserved
configurations, KCONFIG_WERROR, out-of-range values, the supported
probability formats, and the documented empty-value default.
Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
Assisted-by: LLM
Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
---
Changes since v2:
- Move the expected tristate distribution into the problem description.
- Simplify the initial format check to !isdigit((unsigned char)*env).
- Pass probability values and seeds through conf._run_conf(extra_env=...).
Use monkeypatch.delenv only to remove inherited variables.
- Add -0, +0, bare + and - warning cases, and the +101 range-error case.
- Honor KCONFIG_WERROR for malformed format warnings, as discussed with
Julian. Check unset, empty, 0 and 1 flag behavior.
- Compare 50% with 50:0:0 and -0 with 0 across 20 fixed seeds and check
that each malformed input produces exactly one warning.
- Clarify that compatibility applies to malformed in-range inputs with
KCONFIG_WERROR unset; values previously accepted by narrowing now fail.
Changes since v1:
- Warn about malformed in-range values while retaining their previous
interpretation by default.
- Warn about leading whitespace and signs as undocumented input.
- Clarify that 50 gives tristate y/m/n probabilities of 25%/25%/50%, and
compare it with the equivalent input 50:25:25 across 20 fixed seeds.
Validation on kbuild-next, x86_64, GCC 13.3.0:
- make testconfig with HOSTCFLAGS=-Werror: 138 passed. With the unpatched
conf: 74 failed, 64 passed; failures cover missing format warnings,
unchecked narrowing and ignored KCONFIG_WERROR. The previous v3 draft
fails only the nine strict-mode regression cases; 129 tests pass.
- ASan/UBSan at -O1, with leak detection disabled: the same 138 tests passed.
- Both v3 builds matched the submitted v2 on 1,635 inputs: 916 accepted
values and 719 range errors, with KCONFIG_WERROR unset. Each also matched
the original on 380 documented-input comparisons with fixed seeds.
- Each build passed 120 strict format-error checks: no output created in
a fresh directory; existing .config, .config.old and input files stayed
unchanged, including with KCONFIG_ALLCONFIG and KCONFIG_OVERWRITECONFIG.
Valid inputs, range diagnostics and other configuration modes were also
checked with KCONFIG_WERROR set.
- The probability tests also passed with conflicting probability and seed
values and KCONFIG_WERROR=1 inherited from the test runner's environment.
- make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
KCONFIG_PROBABILITY=50% now produces a warning and remains compatible.
- No vmlinux build or boot test; this changes the host configuration tool.
32-bit hosts and other libc implementations were not tested.
scripts/kconfig/conf.c | 16 +-
.../tests/randconfig_probability/Kconfig | 12 ++
.../tests/randconfig_probability/__init__.py | 137 ++++++++++++++++++
3 files changed, 164 insertions(+), 1 deletion(-)
create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index fe8ba09b0..ca6d9d735 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -186,12 +186,23 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
if (mode == def_random) {
int n, p[3];
+ bool warned = false;
char *env = getenv("KCONFIG_PROBABILITY");
n = 0;
while (env && *env) {
char *endp;
- int tmp = strtol(env, &endp, 10);
+ long tmp = strtol(env, &endp, 10);
+
+ if (!isdigit((unsigned char)*env) ||
+ (*endp && *endp != ':') ||
+ (*endp == ':' && (!endp[1] || n == 2))) {
+ if (!warned) {
+ fprintf(stderr,
+ "warning: KCONFIG_PROBABILITY has malformed format\n");
+ warned = true;
+ }
+ }
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
@@ -227,6 +238,9 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
perror("KCONFIG_PROBABILITY");
exit(1);
}
+
+ if (warned && getenv("KCONFIG_WERROR"))
+ exit(1);
}
menu_for_each_entry(menu) {
diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
new file mode 100644
index 000000000..84f4e5fcc
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MODULES
+ bool
+ default y
+ modules
+
+config BOOL
+ bool "Bool"
+
+config TRI
+ tristate "Tristate"
diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
new file mode 100644
index 000000000..76fc39c94
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: GPL-2.0-only
+"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
+
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def clear_werror(monkeypatch):
+ # extra_env can set strict mode, but cannot remove an inherited flag.
+ monkeypatch.delenv('KCONFIG_WERROR', raising=False)
+
+
+@pytest.mark.parametrize('probability', [
+ 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
+ '10:20:invalid', '10:20:30x',
+ ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
+ '-0', '+0', '+', '-', '+100:0', ' \t100:0', '0: \t100:0',
+])
+def test_malformed_warns(conf, probability):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ assert conf.config is not None
+
+
+@pytest.mark.parametrize('probability, equivalent', [
+ ('50%', '50:0:0'),
+ ('-0', '0'),
+])
+@pytest.mark.parametrize('seed', range(20))
+def test_malformed_preserves_config(conf, probability, equivalent, seed):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ malformed = conf.config
+
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': equivalent,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == malformed
+
+
+@pytest.mark.parametrize('werror', ['', '0', '1'])
+@pytest.mark.parametrize('probability, status', [
+ ('', 0), ('50', 0), ('50%', 1), ('-0', 1), ('10:20:30:40', 1),
+])
+def test_werror(conf, probability, status, werror):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ 'KCONFIG_WERROR': werror,
+ }) == status
+ if status:
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ else:
+ assert 'warning:' not in conf.stderr
+
+
+@pytest.mark.parametrize('probability', [
+ '-1', '101', '+101', '0:101', '0:0:101', '60:41', '0:60:41',
+ '4294967296', '-4294967296',
+ '999999999999999999999999', '-999999999999999999999999',
+])
+def test_out_of_range(conf, probability):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 1
+ assert 'KCONFIG_PROBABILITY:' in conf.stderr
+
+
+@pytest.mark.parametrize('probability, boolean, tristate', [
+ ('0', 'n', 'n'),
+ ('0:0', 'n', 'n'),
+ ('100:0', 'y', 'y'),
+ ('0:100', 'y', 'm'),
+ ('100:0:0', 'y', 'n'),
+ ('0:100:0', 'n', 'y'),
+ ('0:0:100', 'n', 'm'),
+ ('000:000:100', 'n', 'm'),
+])
+def test_valid(conf, probability, boolean, tristate):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ for symbol, value in [('BOOL', boolean), ('TRI', tristate)]:
+ if value == 'n':
+ expected = '# CONFIG_{} is not set'.format(symbol)
+ else:
+ expected = 'CONFIG_{}={}'.format(symbol, value)
+ assert expected in conf.config.splitlines()
+
+
+@pytest.mark.parametrize('seed', range(20))
+def test_single_probability_matches_tristate_split(conf, seed):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ single = conf.config
+
+ # 50% boolean y; 25% tristate y, 25% m, and 50% n.
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50:25:25',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == single
+
+
+def test_empty(conf, monkeypatch):
+ # extra_env overrides inherited variables, but cannot remove them.
+ monkeypatch.delenv('KCONFIG_PROBABILITY', raising=False)
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ default_config = conf.config
+
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '',
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == default_config
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-10 12:04 ` [PATCH v3] " Dmitrii Tulnov
@ 2026-09-10 21:17 ` Julian Braha
2026-09-10 21:52 ` [PATCH v4] " Dmitrii Tulnov
0 siblings, 1 reply; 10+ messages in thread
From: Julian Braha @ 2026-09-10 21:17 UTC (permalink / raw)
To: Dmitrii Tulnov, nathan, nsc
Cc: jacmet, yann.morin.1998, linux-kbuild, linux-kernel
On 9/10/26 13:04, Dmitrii Tulnov wrote:
> randconfig checks the numeric range of each probability but does not
> validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
> accepted as 50:0:0: the '%' is parsed repeatedly as zero. For the
> documented value 50, tristate y/m/n probabilities are 25%/25%/50%.
> With 50%, both y/m probabilities silently become zero, reducing the
> coverage of random configuration builds. Empty fields and extra fields
> are also accepted.
>
> Warn when the value does not follow the documented decimal format. For
> malformed inputs whose parsed probabilities are in range, preserve the
> existing interpretation unless KCONFIG_WERROR is set. In that case, exit
> with an error before writing the configuration. Leading whitespace and
> signs are accepted by strtol(), but also trigger the warning because they
> are outside the documented format.
>
> Keep the strtol() result as long until the range check. This rejects
> out-of-range values that narrowing to int previously made valid, such as
> 4294967296 becoming zero on a 64-bit host, regardless of KCONFIG_WERROR.
>
> Add regression tests for one warning per malformed input, preserved
> configurations, KCONFIG_WERROR, out-of-range values, the supported
> probability formats, and the documented empty-value default.
>
> Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
> Assisted-by: LLM
> Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
> ---
> Changes since v2:
> - Move the expected tristate distribution into the problem description.
> - Simplify the initial format check to !isdigit((unsigned char)*env).
> - Pass probability values and seeds through conf._run_conf(extra_env=...).
> Use monkeypatch.delenv only to remove inherited variables.
> - Add -0, +0, bare + and - warning cases, and the +101 range-error case.
> - Honor KCONFIG_WERROR for malformed format warnings, as discussed with
> Julian. Check unset, empty, 0 and 1 flag behavior.
> - Compare 50% with 50:0:0 and -0 with 0 across 20 fixed seeds and check
> that each malformed input produces exactly one warning.
> - Clarify that compatibility applies to malformed in-range inputs with
> KCONFIG_WERROR unset; values previously accepted by narrowing now fail.
>
> Changes since v1:
> - Warn about malformed in-range values while retaining their previous
> interpretation by default.
> - Warn about leading whitespace and signs as undocumented input.
> - Clarify that 50 gives tristate y/m/n probabilities of 25%/25%/50%, and
> compare it with the equivalent input 50:25:25 across 20 fixed seeds.
>
> Validation on kbuild-next, x86_64, GCC 13.3.0:
> - make testconfig with HOSTCFLAGS=-Werror: 138 passed. With the unpatched
> conf: 74 failed, 64 passed; failures cover missing format warnings,
> unchecked narrowing and ignored KCONFIG_WERROR. The previous v3 draft
> fails only the nine strict-mode regression cases; 129 tests pass.
> - ASan/UBSan at -O1, with leak detection disabled: the same 138 tests passed.
> - Both v3 builds matched the submitted v2 on 1,635 inputs: 916 accepted
> values and 719 range errors, with KCONFIG_WERROR unset. Each also matched
> the original on 380 documented-input comparisons with fixed seeds.
> - Each build passed 120 strict format-error checks: no output created in
> a fresh directory; existing .config, .config.old and input files stayed
> unchanged, including with KCONFIG_ALLCONFIG and KCONFIG_OVERWRITECONFIG.
> Valid inputs, range diagnostics and other configuration modes were also
> checked with KCONFIG_WERROR set.
> - The probability tests also passed with conflicting probability and seed
> values and KCONFIG_WERROR=1 inherited from the test runner's environment.
> - make defconfig, allnoconfig, allmodconfig and valid randconfig passed.
> KCONFIG_PROBABILITY=50% now produces a warning and remains compatible.
> - No vmlinux build or boot test; this changes the host configuration tool.
> 32-bit hosts and other libc implementations were not tested.
>
> scripts/kconfig/conf.c | 16 +-
> .../tests/randconfig_probability/Kconfig | 12 ++
> .../tests/randconfig_probability/__init__.py | 137 ++++++++++++++++++
> 3 files changed, 164 insertions(+), 1 deletion(-)
> create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
> create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
>
> diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
> index fe8ba09b0..ca6d9d735 100644
> --- a/scripts/kconfig/conf.c
> +++ b/scripts/kconfig/conf.c
> @@ -186,12 +186,23 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
>
> if (mode == def_random) {
> int n, p[3];
> + bool warned = false;
> char *env = getenv("KCONFIG_PROBABILITY");
>
> n = 0;
> while (env && *env) {
> char *endp;
> - int tmp = strtol(env, &endp, 10);
> + long tmp = strtol(env, &endp, 10);
> +
> + if (!isdigit((unsigned char)*env) ||
> + (*endp && *endp != ':') ||
> + (*endp == ':' && (!endp[1] || n == 2))) {
> + if (!warned) {
> + fprintf(stderr,
> + "warning: KCONFIG_PROBABILITY has malformed format\n");
> + warned = true;
> + }
> + }
>
> if (tmp >= 0 && tmp <= 100) {
> p[n++] = tmp;
> @@ -227,6 +238,9 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
> perror("KCONFIG_PROBABILITY");
> exit(1);
> }
> +
> + if (warned && getenv("KCONFIG_WERROR"))
> + exit(1);
> }
>
> menu_for_each_entry(menu) {
> diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
> new file mode 100644
> index 000000000..84f4e5fcc
> --- /dev/null
> +++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config MODULES
> + bool
> + default y
> + modules
> +
> +config BOOL
> + bool "Bool"
> +
> +config TRI
> + tristate "Tristate"
> diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
> new file mode 100644
> index 000000000..76fc39c94
> --- /dev/null
> +++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
> @@ -0,0 +1,137 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
> +
> +import pytest
> +
> +
> +@pytest.fixture(autouse=True)
> +def clear_werror(monkeypatch):
> + # extra_env can set strict mode, but cannot remove an inherited flag.
> + monkeypatch.delenv('KCONFIG_WERROR', raising=False)
Clearing this environment variable doesn't belong here, it should be
part of scripts/kconfig/tests/conftest.py.
- Julian Braha
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4] kconfig: warn about malformed KCONFIG_PROBABILITY values
2026-09-10 21:17 ` Julian Braha
@ 2026-09-10 21:52 ` Dmitrii Tulnov
0 siblings, 0 replies; 10+ messages in thread
From: Dmitrii Tulnov @ 2026-09-10 21:52 UTC (permalink / raw)
To: nathan, nsc
Cc: julianbraha, jacmet, yann.morin.1998, linux-kbuild, linux-kernel
randconfig checks the numeric range of each probability but does not
validate where strtol() stops. For example, KCONFIG_PROBABILITY=50% is
accepted as 50:0:0: the '%' is parsed repeatedly as zero. For the
documented value 50, tristate y/m/n probabilities are 25%/25%/50%.
With 50%, both y/m probabilities silently become zero, reducing the
coverage of random configuration builds. Empty fields and extra fields
are also accepted.
Warn when the value does not follow the documented decimal format. For
malformed inputs whose parsed probabilities are in range, preserve the
existing interpretation unless KCONFIG_WERROR is set. In that case, exit
with an error before writing the configuration. Leading whitespace and
signs are accepted by strtol(), but also trigger the warning because they
are outside the documented format.
Keep the strtol() result as long until the range check. This rejects
out-of-range values that narrowing to int previously made valid, such as
4294967296 becoming zero on a 64-bit host, regardless of KCONFIG_WERROR.
Add regression tests for one warning per malformed input, preserved
configurations, KCONFIG_WERROR, out-of-range values, the supported
probability formats, and the documented empty-value default.
Fixes: e43956e60769 ("kconfig: implement KCONFIG_PROBABILITY for randconfig")
Assisted-by: LLM
Signed-off-by: Dmitrii Tulnov <tulnov.dl@gmail.com>
---
Thanks, Julian. I've moved the KCONFIG_WERROR cleanup fixture to
scripts/kconfig/tests/conftest.py.
Changes since v3:
- Move the KCONFIG_WERROR cleanup fixture to the shared conftest.py,
as requested by Julian. Explicit extra_env settings still enable WERROR.
Changes since v2:
- Move the expected tristate distribution into the problem description.
- Simplify the initial format check to !isdigit((unsigned char)*env).
- Pass probability values and seeds through conf._run_conf(extra_env=...).
Use monkeypatch.delenv only to remove inherited variables.
- Add -0, +0, bare + and - warning cases, and the +101 range-error case.
- Honor KCONFIG_WERROR for malformed format warnings, as discussed with
Julian. Check unset, empty, 0 and 1 flag behavior.
- Compare 50% with 50:0:0 and -0 with 0 across 20 fixed seeds and check
that each malformed input produces exactly one warning.
- Clarify that compatibility applies to malformed in-range inputs with
KCONFIG_WERROR unset; values previously accepted by narrowing now fail.
Changes since v1:
- Warn about malformed in-range values while retaining their previous
interpretation by default.
- Warn about leading whitespace and signs as undocumented input.
- Clarify that 50 gives tristate y/m/n probabilities of 25%/25%/50%, and
compare it with the equivalent input 50:25:25 across 20 fixed seeds.
Validation on kbuild-next, x86_64, GCC 13.3.0:
- make testconfig with HOSTCFLAGS=-Werror: 138 passed. The same 138 tests
passed with KCONFIG_WERROR=1 inherited from the test runner, including
the explicit strict-mode cases for empty, 0 and 1 flag values.
- The same two runs passed on the existing ASan/UBSan build at -O1, with
leak detection disabled: 138 passed in each run.
- C code, test inputs and assertions are unchanged from the submitted v3.
Its earlier checks remain applicable: 74 regression failures on the
original; 1,635 comparisons with v2 and 380 documented-input comparisons
with the original per build; 120 strict error file-preservation checks
per build, including ALLCONFIG and OVERWRITECONFIG. These broader C
checks were not rerun for the fixture move.
- No vmlinux build or boot test; this changes the host configuration tool.
32-bit hosts and other libc implementations were not tested.
scripts/kconfig/conf.c | 16 ++-
scripts/kconfig/tests/conftest.py | 6 +
.../tests/randconfig_probability/Kconfig | 12 ++
.../tests/randconfig_probability/__init__.py | 131 ++++++++++++++++++
4 files changed, 164 insertions(+), 1 deletion(-)
create mode 100644 scripts/kconfig/tests/randconfig_probability/Kconfig
create mode 100644 scripts/kconfig/tests/randconfig_probability/__init__.py
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index fe8ba09b0..ca6d9d735 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -186,12 +186,23 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
if (mode == def_random) {
int n, p[3];
+ bool warned = false;
char *env = getenv("KCONFIG_PROBABILITY");
n = 0;
while (env && *env) {
char *endp;
- int tmp = strtol(env, &endp, 10);
+ long tmp = strtol(env, &endp, 10);
+
+ if (!isdigit((unsigned char)*env) ||
+ (*endp && *endp != ':') ||
+ (*endp == ':' && (!endp[1] || n == 2))) {
+ if (!warned) {
+ fprintf(stderr,
+ "warning: KCONFIG_PROBABILITY has malformed format\n");
+ warned = true;
+ }
+ }
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
@@ -227,6 +238,9 @@ static void conf_set_all_new_symbols(enum conf_def_mode mode)
perror("KCONFIG_PROBABILITY");
exit(1);
}
+
+ if (warned && getenv("KCONFIG_WERROR"))
+ exit(1);
}
menu_for_each_entry(menu) {
diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py
index 66f95e4ed..2263bd2a3 100644
--- a/scripts/kconfig/tests/conftest.py
+++ b/scripts/kconfig/tests/conftest.py
@@ -312,6 +312,12 @@ class Conf:
return self._matches('stderr', expected)
+@pytest.fixture(autouse=True)
+def clear_werror(monkeypatch):
+ # extra_env can set strict mode, but cannot remove an inherited flag.
+ monkeypatch.delenv('KCONFIG_WERROR', raising=False)
+
+
@pytest.fixture(scope="module")
def conf(request):
"""Create a Conf instance and provide it to test functions."""
diff --git a/scripts/kconfig/tests/randconfig_probability/Kconfig b/scripts/kconfig/tests/randconfig_probability/Kconfig
new file mode 100644
index 000000000..84f4e5fcc
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MODULES
+ bool
+ default y
+ modules
+
+config BOOL
+ bool "Bool"
+
+config TRI
+ tristate "Tristate"
diff --git a/scripts/kconfig/tests/randconfig_probability/__init__.py b/scripts/kconfig/tests/randconfig_probability/__init__.py
new file mode 100644
index 000000000..78d367a9c
--- /dev/null
+++ b/scripts/kconfig/tests/randconfig_probability/__init__.py
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: GPL-2.0-only
+"""Validate KCONFIG_PROBABILITY without changing the supported distributions."""
+
+import pytest
+
+
+@pytest.mark.parametrize('probability', [
+ 'invalid', ' ', '50%', '50 ', '0x32', '10 20', '10:20x',
+ '10:20:invalid', '10:20:30x',
+ ':50', '50:', '10::20', '10:20:', '10:20:30:', '10:20:30:40',
+ '-0', '+0', '+', '-', '+100:0', ' \t100:0', '0: \t100:0',
+])
+def test_malformed_warns(conf, probability):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ assert conf.config is not None
+
+
+@pytest.mark.parametrize('probability, equivalent', [
+ ('50%', '50:0:0'),
+ ('-0', '0'),
+])
+@pytest.mark.parametrize('seed', range(20))
+def test_malformed_preserves_config(conf, probability, equivalent, seed):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ malformed = conf.config
+
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': equivalent,
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == malformed
+
+
+@pytest.mark.parametrize('werror', ['', '0', '1'])
+@pytest.mark.parametrize('probability, status', [
+ ('', 0), ('50', 0), ('50%', 1), ('-0', 1), ('10:20:30:40', 1),
+])
+def test_werror(conf, probability, status, werror):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ 'KCONFIG_WERROR': werror,
+ }) == status
+ if status:
+ assert 'warning: KCONFIG_PROBABILITY has malformed format' in conf.stderr
+ assert conf.stderr.count('warning:') == 1
+ else:
+ assert 'warning:' not in conf.stderr
+
+
+@pytest.mark.parametrize('probability', [
+ '-1', '101', '+101', '0:101', '0:0:101', '60:41', '0:60:41',
+ '4294967296', '-4294967296',
+ '999999999999999999999999', '-999999999999999999999999',
+])
+def test_out_of_range(conf, probability):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 1
+ assert 'KCONFIG_PROBABILITY:' in conf.stderr
+
+
+@pytest.mark.parametrize('probability, boolean, tristate', [
+ ('0', 'n', 'n'),
+ ('0:0', 'n', 'n'),
+ ('100:0', 'y', 'y'),
+ ('0:100', 'y', 'm'),
+ ('100:0:0', 'y', 'n'),
+ ('0:100:0', 'n', 'y'),
+ ('0:0:100', 'n', 'm'),
+ ('000:000:100', 'n', 'm'),
+])
+def test_valid(conf, probability, boolean, tristate):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': probability,
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ for symbol, value in [('BOOL', boolean), ('TRI', tristate)]:
+ if value == 'n':
+ expected = '# CONFIG_{} is not set'.format(symbol)
+ else:
+ expected = 'CONFIG_{}={}'.format(symbol, value)
+ assert expected in conf.config.splitlines()
+
+
+@pytest.mark.parametrize('seed', range(20))
+def test_single_probability_matches_tristate_split(conf, seed):
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ single = conf.config
+
+ # 50% boolean y; 25% tristate y, 25% m, and 50% n.
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '50:25:25',
+ 'KCONFIG_SEED': hex(seed),
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == single
+
+
+def test_empty(conf, monkeypatch):
+ # extra_env overrides inherited variables, but cannot remove them.
+ monkeypatch.delenv('KCONFIG_PROBABILITY', raising=False)
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ default_config = conf.config
+
+ assert conf._run_conf('--randconfig', extra_env={
+ 'KCONFIG_PROBABILITY': '',
+ 'KCONFIG_SEED': '0',
+ }) == 0
+ assert 'warning:' not in conf.stderr
+ assert conf.config == default_config
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-09-10 21:52 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-07 19:45 [PATCH] kconfig: reject malformed KCONFIG_PROBABILITY values Dmitrii Tulnov
2026-09-08 19:24 ` Julian Braha
2026-09-08 20:35 ` Dmitrii Tulnov
2026-09-08 20:36 ` [PATCH v2] kconfig: warn about " Dmitrii Tulnov
2026-09-10 0:05 ` Julian Braha
2026-09-10 9:13 ` Dmitrii Tulnov
2026-09-10 11:25 ` Julian Braha
2026-09-10 12:04 ` [PATCH v3] " Dmitrii Tulnov
2026-09-10 21:17 ` Julian Braha
2026-09-10 21:52 ` [PATCH v4] " Dmitrii Tulnov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®