[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <7bca36d46dab04667aa595623fd0966385ee4658@intel.com>
Date: Mon, 01 Sep 2025 12:09:14 +0300
From: Jani Nikula <jani.nikula@...ux.intel.com>
To: Kees Cook <kees@...nel.org>, Nathan Chancellor <nathan@...nel.org>
Cc: Kees Cook <kees@...nel.org>, Nicolas Schier <nicolas.schier@...ux.dev>,
Jonathan Corbet <corbet@....net>, Masahiro Yamada <masahiroy@...nel.org>,
Randy Dunlap <rdunlap@...radead.org>, Arnd Bergmann <arnd@...db.de>,
Krzysztof Kozlowski <krzysztof.kozlowski@...aro.org>,
linux-kbuild@...r.kernel.org, linux-doc@...r.kernel.org, Miguel Ojeda
<ojeda@...nel.org>, Stephen Brennan <stephen.s.brennan@...cle.com>, Marco
Bonelli <marco@...eim.net>, Petr Vorel <pvorel@...e.cz>,
linux-kernel@...r.kernel.org, linux-hardening@...r.kernel.org
Subject: Re: [PATCH v2] kconfig: Add transitional symbol attribute for
migration support
On Fri, 29 Aug 2025, Kees Cook <kees@...nel.org> wrote:
> During kernel option migrations (e.g. CONFIG_CFI_CLANG to CONFIG_CFI),
> existing .config files need to maintain backward compatibility while
> preventing deprecated options from appearing in newly generated
> configurations. This is challenging with existing Kconfig mechanisms
> because:
>
> 1. Simply removing old options breaks existing .config files.
> 2. Manually listing an option as "deprecated" leaves it needlessly
> visible and still writes them to new .config files.
> 3. Using any method to remove visibility (.e.g no 'prompt', 'if n',
> etc) prevents the option from being processed at all.
>
> Add a "transitional" attribute that creates symbols which are:
> - Processed during configuration (can influence other symbols' defaults)
> - Hidden from user menus (no prompts appear)
> - Omitted from newly written .config files (gets migrated)
> - Restricted to only having help sections (no defaults, selects, etc)
> making it truly just a "prior value pass-through" option.
>
> The transitional syntax requires a type argument and prevents type
> redefinition:
>
> config OLD_OPTION
> transitional bool
> help
> Transitional config for OLD_OPTION migration.
How long do you think we'll need to keep the transitional config options
around? Forever?
BR,
Jani.
> config NEW_OPTION
> bool "New option"
> default OLD_OPTION
>
> This allows seamless migration: olddefconfig processes existing
> CONFIG_OLD_OPTION=y settings to enable CONFIG_NEW_OPTION=y, while
> CONFIG_OLD_OPTION is omitted from newly generated .config files.
>
> Implementation details:
> - Parser validates transitional symbols can only have help sections
> - Symbol visibility logic updated: usable = (visible != no || transitional)
> - Transitional symbols preserve user values during configuration
> - Type safety enforced to prevent redefinition after transitional declaration
> - Used distinct struct members instead of new flags for readability
> - Documentation added to show the usage
>
> Signed-off-by: Kees Cook <kees@...nel.org>
> ---
> With help from Claude Code to show me how to navigate the kconfig parser.
>
> v2: fixed human-introduced errors
> v1: https://lore.kernel.org/all/20250830014438.work.682-kees@kernel.org/
>
> Cc: Nathan Chancellor <nathan@...nel.org>
> Cc: Nicolas Schier <nicolas.schier@...ux.dev>
> Cc: Jonathan Corbet <corbet@....net>
> Cc: Masahiro Yamada <masahiroy@...nel.org>
> Cc: Randy Dunlap <rdunlap@...radead.org>
> Cc: Arnd Bergmann <arnd@...db.de>
> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@...aro.org>
> Cc: <linux-kbuild@...r.kernel.org>
> Cc: <linux-doc@...r.kernel.org>
> ---
> scripts/kconfig/expr.h | 15 +++++++
> scripts/kconfig/lexer.l | 1 +
> scripts/kconfig/parser.y | 51 +++++++++++++++++++++++
> scripts/kconfig/symbol.c | 11 +++--
> Documentation/kbuild/kconfig-language.rst | 31 ++++++++++++++
> 5 files changed, 106 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
> index fe2231e0e6a4..be51574d6c77 100644
> --- a/scripts/kconfig/expr.h
> +++ b/scripts/kconfig/expr.h
> @@ -127,6 +127,21 @@ struct symbol {
> /* SYMBOL_* flags */
> int flags;
>
> + /*
> + * Transitional symbol - processed during configuration but hidden from
> + * user in menus and omitted from newly written .config files. Used for
> + * backward compatibility during config option migrations (e.g.,
> + * CFI_CLANG → CFI). Transitional symbols can still influence default
> + * expressions of other symbols.
> + */
> + bool transitional:1;
> +
> + /*
> + * Symbol usability - calculated as (visible != no || transitional).
> + * Determines if symbol can be used in expressions.
> + */
> + bool usable:1;
> +
> /* List of properties. See prop_type. */
> struct property *prop;
>
> diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
> index 9c2cdfc33c6f..6d2c92c6095d 100644
> --- a/scripts/kconfig/lexer.l
> +++ b/scripts/kconfig/lexer.l
> @@ -126,6 +126,7 @@ n [A-Za-z0-9_-]
> "select" return T_SELECT;
> "source" return T_SOURCE;
> "string" return T_STRING;
> +"transitional" return T_TRANSITIONAL;
> "tristate" return T_TRISTATE;
> "visible" return T_VISIBLE;
> "||" return T_OR;
> diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
> index e9c3c664e925..01d2d0f720ce 100644
> --- a/scripts/kconfig/parser.y
> +++ b/scripts/kconfig/parser.y
> @@ -75,6 +75,7 @@ struct menu *current_menu, *current_entry, *current_choice;
> %token T_SELECT
> %token T_SOURCE
> %token T_STRING
> +%token T_TRANSITIONAL
> %token T_TRISTATE
> %token T_VISIBLE
> %token T_EOL
> @@ -205,6 +206,16 @@ config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
> printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
> };
>
> +config_option: T_TRANSITIONAL type T_EOL
> +{
> + if (current_entry->sym->type != S_UNKNOWN)
> + yyerror("transitional type cannot be set after symbol type is already defined");
> + menu_set_type($2);
> + current_entry->sym->transitional = true;
> + printd(DEBUG_PARSE, "%s:%d:transitional(%u)\n", cur_filename, cur_lineno,
> + $2);
> +};
> +
> config_option: default expr if_expr T_EOL
> {
> menu_add_expr(P_DEFAULT, $2, $3);
> @@ -482,6 +493,43 @@ assign_val:
>
> %%
>
> +/**
> + * transitional_check_sanity - check transitional symbols have no other
> + * properties
> + *
> + * @menu: menu of the potentially transitional symbol
> + *
> + * Return: -1 if an error is found, 0 otherwise.
> + */
> +static int transitional_check_sanity(const struct menu *menu)
> +{
> + struct property *prop;
> +
> + if (!menu->sym || !menu->sym->transitional)
> + return 0;
> +
> + /* Check for depends and visible conditions. */
> + if ((menu->dep && !expr_is_yes(menu->dep)) ||
> + (menu->visibility && !expr_is_yes(menu->visibility))) {
> + fprintf(stderr, "%s:%d: error: %s",
> + menu->filename, menu->lineno,
> + "transitional symbols can only have help sections\n");
> + return -1;
> + }
> +
> + /* Check for any property other than "help". */
> + for (prop = menu->sym->prop; prop; prop = prop->next) {
> + if (prop->type != P_COMMENT) {
> + fprintf(stderr, "%s:%d: error: %s",
> + prop->filename, prop->lineno,
> + "transitional symbols can only have help sections\n");
> + return -1;
> + }
> + }
> +
> + return 0;
> +}
> +
> /**
> * choice_check_sanity - check sanity of a choice member
> *
> @@ -558,6 +606,9 @@ void conf_parse(const char *name)
> if (menu->sym && sym_check_deps(menu->sym))
> yynerrs++;
>
> + if (transitional_check_sanity(menu))
> + yynerrs++;
> +
> if (menu->sym && sym_is_choice(menu->sym)) {
> menu_for_each_sub_entry(child, menu)
> if (child->sym && choice_check_sanity(child))
> diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
> index 26ab10c0fd76..b822c0c897e5 100644
> --- a/scripts/kconfig/symbol.c
> +++ b/scripts/kconfig/symbol.c
> @@ -447,6 +447,9 @@ void sym_calc_value(struct symbol *sym)
> if (sym->visible != no)
> sym->flags |= SYMBOL_WRITE;
>
> + /* Calculate usable flag */
> + sym->usable = (sym->visible != no || sym->transitional);
> +
> /* set default if recursively called */
> sym->curr = newval;
>
> @@ -459,13 +462,15 @@ void sym_calc_value(struct symbol *sym)
> sym_calc_choice(choice_menu);
> newval.tri = sym->curr.tri;
> } else {
> - if (sym->visible != no) {
> + if (sym->usable) {
> /* if the symbol is visible use the user value
> * if available, otherwise try the default value
> */
> if (sym_has_value(sym)) {
> + tristate value = sym->transitional ?
> + sym->def[S_DEF_USER].tri : sym->visible;
> newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,
> - sym->visible);
> + value);
> goto calc_newval;
> }
> }
> @@ -497,7 +502,7 @@ void sym_calc_value(struct symbol *sym)
> case S_STRING:
> case S_HEX:
> case S_INT:
> - if (sym->visible != no && sym_has_value(sym)) {
> + if (sym->usable && sym_has_value(sym)) {
> newval.val = sym->def[S_DEF_USER].val;
> break;
> }
> diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst
> index a91abb8f6840..345c334ce680 100644
> --- a/Documentation/kbuild/kconfig-language.rst
> +++ b/Documentation/kbuild/kconfig-language.rst
> @@ -232,6 +232,37 @@ applicable everywhere (see syntax).
> enables the third modular state for all config symbols.
> At most one symbol may have the "modules" option set.
>
> +- transitional attribute: "transitional"
> + This declares the symbol as transitional, meaning it should be processed
> + during configuration but omitted from newly written .config files.
> + Transitional symbols are useful for backward compatibility during config
> + option migrations - they allow olddefconfig to process existing .config
> + files while ensuring the old option doesn't appear in new configurations.
> +
> + A transitional symbol:
> + - Has no prompt (is not visible to users in menus)
> + - Is processed normally during configuration (values are read and used)
> + - Can be referenced in default expressions of other symbols
> + - Is not written to new .config files
> + - Cannot have any other properties (it is a pass-through option)
> +
> + Example migration from OLD_NAME to NEW_NAME::
> +
> + config NEW_NAME
> + bool "New option name"
> + default OLD_NAME
> + help
> + This replaces the old CONFIG_OLD_NAME option.
> +
> + config OLD_NAME
> + transitional bool
> + help
> + Transitional config for OLD_NAME to NEW_NAME migration.
> +
> + With this setup, existing .config files with "CONFIG_OLD_NAME=y" will
> + result in "CONFIG_NEW_NAME=y" being set, while CONFIG_OLD_NAME will be
> + omitted from newly written .config files.
> +
> Menu dependencies
> -----------------
--
Jani Nikula, Intel
Powered by blists - more mailing lists