diff mbox series

kconfig: fix comparison to constant symbols, 'm', 'n'

Message ID 20240519092227.2101109-1-masahiroy@kernel.org (mailing list archive)
State New
Headers show
Series kconfig: fix comparison to constant symbols, 'm', 'n' | expand

Commit Message

Masahiro Yamada May 19, 2024, 9:22 a.m. UTC
Currently, comparisons to 'm' or 'n' result in incorrect output.

[Test Code]

    config MODULES
            def_bool y
            modules

    config A
            def_tristate m

    config B
            def_bool A > n

CONFIG_B is actually unset, while the expectation is CONFIG_B=y.

The reason for the issue is because Kconfig compares the tristate values
as strings.

Currently, the .type fields of the constant symbols, 'y', 'm', and 'n'
are unspecified, i.e., S_UNKNOWN.

When expr_calc_value() evaluates 'A > n', it checks the types of 'A' and
'n' to determine how to compare them.

The left-hand side, 'A', is a tristate symbol with a value of 'm', which
corresponds to a numeric value of 1. (Internally, 'y', 'm', and 'n' are
represented as 2, 1, and 0, respectively.)

The right-hand side, 'n', has an unknown type, so it is treated as the
string "n" during the comparison.

expr_calc_value() compares two values numerically only when both can
have numeric values. Otherwise, they are compared as strings.

    symbol    numeric value    ASCII code
    -------------------------------------
      y           2             0x79
      m           1             0x6d
      n           0             0x6e

'm' is greater than 'n' if compared numerically (since 1 is greater
than 0), but small than 'n' if compared as strings (since the ASCII
code 0x6d is smaller than 0x6e).

Specifying .type=S_TRISTATE for symbol_{yes,mod,no} fixes the above
test code.

However, this would cause a regression to the following test code.

[Test Code 2]

    config MODULES
            def_bool n
            modules

    config A
            def_tristate n

    config B
            def_bool A = m

You would get CONFIG_B=y, while CONFIG_B should not be set.

The reason is because sym_get_string_value() turns 'm' into 'n' when
the module feature is disabled. Consequently, expr_calc_value() returns
the result of 'A = n' instead of 'A = m'. This oddity has been hidden
because the type of 'm' was previously S_UNKNOWN instead of S_TRISTATE.

sym_get_string_value() should not tweak the string because the tristate
value has already been correctly calculated. There is no reason to
return the string "n" where its tristate value is mod.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
---

 scripts/kconfig/symbol.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Comments

Masahiro Yamada May 19, 2024, 9:34 a.m. UTC | #1
On Sun, May 19, 2024 at 6:22 PM Masahiro Yamada <masahiroy@kernel.org> wrote:
>
> Currently, comparisons to 'm' or 'n' result in incorrect output.
>
> [Test Code]
>
>     config MODULES
>             def_bool y
>             modules
>
>     config A
>             def_tristate m
>
>     config B
>             def_bool A > n
>
> CONFIG_B is actually unset, while the expectation is CONFIG_B=y.
>
> The reason for the issue is because Kconfig compares the tristate values
> as strings.
>
> Currently, the .type fields of the constant symbols, 'y', 'm', and 'n'
> are unspecified, i.e., S_UNKNOWN.
>
> When expr_calc_value() evaluates 'A > n', it checks the types of 'A' and
> 'n' to determine how to compare them.
>
> The left-hand side, 'A', is a tristate symbol with a value of 'm', which
> corresponds to a numeric value of 1. (Internally, 'y', 'm', and 'n' are
> represented as 2, 1, and 0, respectively.)
>
> The right-hand side, 'n', has an unknown type, so it is treated as the
> string "n" during the comparison.
>
> expr_calc_value() compares two values numerically only when both can
> have numeric values. Otherwise, they are compared as strings.
>
>     symbol    numeric value    ASCII code
>     -------------------------------------
>       y           2             0x79
>       m           1             0x6d
>       n           0             0x6e
>
> 'm' is greater than 'n' if compared numerically (since 1 is greater
> than 0), but small than 'n' if compared as strings (since the ASCII
> code 0x6d is smaller than 0x6e).
>
> Specifying .type=S_TRISTATE for symbol_{yes,mod,no} fixes the above
> test code.
>
> However, this would cause a regression to the following test code.
>
> [Test Code 2]
>
>     config MODULES
>             def_bool n
>             modules
>
>     config A
>             def_tristate n
>
>     config B
>             def_bool A = m
>
> You would get CONFIG_B=y, while CONFIG_B should not be set.
>
> The reason is because sym_get_string_value() turns 'm' into 'n' when
> the module feature is disabled. Consequently, expr_calc_value() returns
> the result of 'A = n' instead of 'A = m'. This oddity has been hidden
> because the type of 'm' was previously S_UNKNOWN instead of S_TRISTATE.
>
> sym_get_string_value() should not tweak the string because the tristate
> value has already been correctly calculated. There is no reason to
> return the string "n" where its tristate value is mod.
>
> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
> ---


Fixes: 31847b67bec0 ("kconfig: allow use of relations other than (in)equality")
Masahiro Yamada May 19, 2024, 9:50 a.m. UTC | #2
On Sun, May 19, 2024 at 6:22 PM Masahiro Yamada <masahiroy@kernel.org> wrote:
>
> Currently, comparisons to 'm' or 'n' result in incorrect output.
>
> [Test Code]
>
>     config MODULES
>             def_bool y
>             modules
>
>     config A
>             def_tristate m
>
>     config B
>             def_bool A > n
>
> CONFIG_B is actually unset, while the expectation is CONFIG_B=y.
>
> The reason for the issue is because Kconfig compares the tristate values
> as strings.
>
> Currently, the .type fields of the constant symbols, 'y', 'm', and 'n'
> are unspecified, i.e., S_UNKNOWN.
>
> When expr_calc_value() evaluates 'A > n', it checks the types of 'A' and
> 'n' to determine how to compare them.
>
> The left-hand side, 'A', is a tristate symbol with a value of 'm', which
> corresponds to a numeric value of 1. (Internally, 'y', 'm', and 'n' are
> represented as 2, 1, and 0, respectively.)
>
> The right-hand side, 'n', has an unknown type, so it is treated as the
> string "n" during the comparison.
>
> expr_calc_value() compares two values numerically only when both can
> have numeric values. Otherwise, they are compared as strings.
>
>     symbol    numeric value    ASCII code
>     -------------------------------------
>       y           2             0x79
>       m           1             0x6d
>       n           0             0x6e
>
> 'm' is greater than 'n' if compared numerically (since 1 is greater
> than 0), but small than 'n' if compared as strings (since the ASCII


small than -> smaller than
diff mbox series

Patch

diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index aa0e25ee5119..0e439d3d48d1 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -14,6 +14,7 @@ 
 
 struct symbol symbol_yes = {
 	.name = "y",
+	.type = S_TRISTATE,
 	.curr = { "y", yes },
 	.menus = LIST_HEAD_INIT(symbol_yes.menus),
 	.flags = SYMBOL_CONST|SYMBOL_VALID,
@@ -21,6 +22,7 @@  struct symbol symbol_yes = {
 
 struct symbol symbol_mod = {
 	.name = "m",
+	.type = S_TRISTATE,
 	.curr = { "m", mod },
 	.menus = LIST_HEAD_INIT(symbol_mod.menus),
 	.flags = SYMBOL_CONST|SYMBOL_VALID,
@@ -28,6 +30,7 @@  struct symbol symbol_mod = {
 
 struct symbol symbol_no = {
 	.name = "n",
+	.type = S_TRISTATE,
 	.curr = { "n", no },
 	.menus = LIST_HEAD_INIT(symbol_no.menus),
 	.flags = SYMBOL_CONST|SYMBOL_VALID,
@@ -820,8 +823,7 @@  const char *sym_get_string_value(struct symbol *sym)
 		case no:
 			return "n";
 		case mod:
-			sym_calc_value(modules_sym);
-			return (modules_sym->curr.tri == no) ? "n" : "m";
+			return "m";
 		case yes:
 			return "y";
 		}