[PATCH] kconfig: Error out on duplicated kconfig inclusion

From: Nicolas Schier

Date: Fri Feb 20 2026 - 13:55:45 EST


Let kconfig exit with error on duplicated Kconfig file inclusion.

Repeated inclusion of Kbuild files are considered bad-practise with
regard to maintenance; and Kconfig language is rich enough that there
should be no need for that.

If repeated inclusion of Kconfig files is detected, error out with
messages like:

Kconfig.inc1:4: error: Repeated inclusion of Kconfig.inc3
Kconfig.inc2:3: note: Location of first inclusion of Kconfig.inc3

While commit f094f8a1b273 ("kconfig: allow multiple inclusion of the
same file") introduced detection of recursive inclusions of Kconfig
files, it explicitly allowed repeated inclusions, unfortunately w/o
reasoning.

Reported-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Closes: https://lore.kernel.org/all/CAHk-=wj03hLzK2D=+OYmjgcmGM+XYymp8GyaEs=C0=rXG2nb7w@xxxxxxxxxxxxxx/
Signed-off-by: Nicolas Schier <nsc@xxxxxxxxxx>
---
scripts/kconfig/lexer.l | 4 +--
scripts/kconfig/lkc.h | 3 ++-
scripts/kconfig/tests/err_repeated_inc/Kconfig | 3 +++
.../kconfig/tests/err_repeated_inc/Kconfig.inc1 | 4 +++
.../kconfig/tests/err_repeated_inc/Kconfig.inc2 | 3 +++
.../kconfig/tests/err_repeated_inc/Kconfig.inc3 | 1 +
scripts/kconfig/tests/err_repeated_inc/__init__.py | 10 +++++++
.../kconfig/tests/err_repeated_inc/expected_stderr | 2 ++
scripts/kconfig/util.c | 31 +++++++++++++++++++---
9 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
index 6d2c92c6095dd8c247e6c2dd5dd0b56b8ea6a769..a6155422b4a688070a40a88edfedbcacb251da76 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -402,7 +402,7 @@ void zconf_initscan(const char *name)
exit(1);
}

- cur_filename = file_lookup(name);
+ cur_filename = file_lookup(name, NULL, 0);
yylineno = 1;
}

@@ -443,7 +443,7 @@ void zconf_nextfile(const char *name)
}

yylineno = 1;
- cur_filename = file_lookup(name);
+ cur_filename = file_lookup(name, cur_filename, cur_lineno);
}

static void zconf_endfile(void)
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index 79898596121563350d2fa63fcf879743b756d003..7e6f6ca299cf1a74e4aed704972bc098616a94b0 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -51,7 +51,8 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}

/* util.c */
-const char *file_lookup(const char *name);
+const char *file_lookup(const char *name,
+ const char *parent_name, int parent_lineno);

/* lexer.l */
int yylex(void);
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig b/scripts/kconfig/tests/err_repeated_inc/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..09a88fd29cb5a90f50585c9381a3f1b2f64a6087
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc1"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
new file mode 100644
index 0000000000000000000000000000000000000000..495dc38314a1ac0303f9ce23500af8b009eaad3d
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc2"
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
new file mode 100644
index 0000000000000000000000000000000000000000..2b630eec2e99163cd04bc9d3e55393c70f8a886c
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
new file mode 100644
index 0000000000000000000000000000000000000000..a4e40e534e6a84db241abfe5076962a90f8a71bd
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
@@ -0,0 +1 @@
+# SPDX-License-Identifier: GPL-2.0-only
diff --git a/scripts/kconfig/tests/err_repeated_inc/__init__.py b/scripts/kconfig/tests/err_repeated_inc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..129d740a874b30e73acde797bd00e9f160c7a7ef
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/__init__.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Detect repeated inclusion error.
+
+If repeated inclusion is detected, it should fail with error message.
+"""
+
+def test(conf):
+ assert conf.oldaskconfig() != 0
+ assert conf.stderr_contains('expected_stderr')
diff --git a/scripts/kconfig/tests/err_repeated_inc/expected_stderr b/scripts/kconfig/tests/err_repeated_inc/expected_stderr
new file mode 100644
index 0000000000000000000000000000000000000000..95d90d6a93c52ec886a6309600c8c88205253497
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/expected_stderr
@@ -0,0 +1,2 @@
+Kconfig.inc1:4: error: Repeated inclusion of Kconfig.inc3
+Kconfig.inc2:3: note: Location of first inclusion of Kconfig.inc3
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 5cdcee144b58126dcfab50fd709be9fc40e99423..1b69dd9e1872a0671a70e24b45a30e449dd2c75e 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -18,25 +18,50 @@ static HASHTABLE_DEFINE(file_hashtable, 1U << 11);

struct file {
struct hlist_node node;
+ struct {
+ const char *name;
+ int lineno;
+ } parent;
char name[];
};

+static void die_duplicated_include(struct file *file,
+ const char *parent, int lineno)
+{
+ fprintf(stderr,
+ "%s:%d: error: Repeated inclusion of %s\n"
+ "%s:%d: note: Location of first inclusion of %s\n",
+ parent, lineno, file->name,
+ file->parent.name, file->parent.lineno, file->name);
+ exit(1);
+}
+
/* file already present in list? If not add it */
-const char *file_lookup(const char *name)
+const char *file_lookup(const char *name,
+ const char *parent_name, int parent_lineno)
{
+ const char *parent = NULL;
struct file *file;
size_t len;
int hash = hash_str(name);

+ if (parent_name)
+ parent = file_lookup(parent_name, NULL, 0);
+
hash_for_each_possible(file_hashtable, file, node, hash)
- if (!strcmp(name, file->name))
- return file->name;
+ if (!strcmp(name, file->name)) {
+ if (!parent_name)
+ return file->name;
+ die_duplicated_include(file, parent, parent_lineno);
+ }

len = strlen(name);
file = xmalloc(sizeof(*file) + len + 1);
memset(file, 0, sizeof(*file));
memcpy(file->name, name, len);
file->name[len] = '\0';
+ file->parent.name = parent;
+ file->parent.lineno = parent_lineno;

hash_add(file_hashtable, &file->node, hash);


---
base-commit: ca4ee40bf13dbd3a4be3b40a00c33a1153d487e5
change-id: 20260218-kconfig-error-out-on-duplicated-inclusion-5e96d6b7ef44

Best regards,
--
Nicolas