* [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
@ 2026-05-10 6:13 Aaron Tomlin
2026-05-11 20:34 ` Aaron Tomlin
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Aaron Tomlin @ 2026-05-10 6:13 UTC (permalink / raw)
To: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen
Cc: atomlin, neelx, sean, chjohnst, steve, mproche, nick.lane,
linux-arch, linux-modules, linux-kernel
At present, identifying the correct function name to supply to the
"initcall_blacklist=" kernel command-line parameter requires manual
inspection of the source code or kernel symbol tables. Furthermore,
administrators lack a reliable runtime mechanism to verify whether a
specified built-in module has been successfully blacklisted.
To resolve this, introduce a new debugfs interface at
/sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
built-in modules alongside their corresponding initialisation callbacks
(e.g., those specified by module_init()) in a simple format:
"module_name init_callback". If a built-in module has been actively
blacklisted, the entry is explicitly appended with a " [blacklisted]"
suffix.
To achieve this without incurring runtime overhead, the
___define_initcall macro is expanded to statically allocate a
builtin_initcall_record structure (containing the module and function
name strings) within a dedicated .rodata_builtin_initcall_records
section.
This implementation incorporates several critical architectural safety
measures:
- The custom section name deliberately begins with ".rodata" to ensure
KASLR (Kernel Address Space Layout Randomisation) relocation tools
process and update the string pointers correctly during boot.
- The structural alignment is explicitly forced to 8 bytes to prevent
the compiler from inserting silent padding that would misalign the
linker script boundaries.
- The blacklisted_initcalls list head is stripped of its
__initdata_or_module annotation, ensuring the list remains safely
accessible in memory when queried from user space after early boot.
The creation of the debugfs file and the associated memory allocations
are strictly guarded by CONFIG_DEBUG_FS and CONFIG_KALLSYMS, as the
underlying initcall blacklisting mechanism is entirely dependent on
kallsyms being present.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
include/asm-generic/vmlinux.lds.h | 4 +++
include/linux/init.h | 21 ++++++++++++
init/main.c | 55 ++++++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..d0fb971a8d5c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -483,6 +483,10 @@
SCHED_DATA \
RO_AFTER_INIT_DATA /* Read only after init */ \
. = ALIGN(8); \
+ __builtin_initcall_records_start = .; \
+ KEEP(*(.rodata_builtin_initcall_records)) \
+ __builtin_initcall_records_end = .; \
+ . = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
} \
diff --git a/include/linux/init.h b/include/linux/init.h
index 40331923b9f4..d682fe0d6064 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -182,6 +182,16 @@ extern struct module __this_module;
#ifndef __ASSEMBLY__
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+#ifndef KBUILD_MODNAME
+#define KBUILD_MODNAME "unknown"
+#endif
+struct builtin_initcall_record {
+ const char *modname;
+ const char *fnname;
+};
+#endif
+
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
@@ -271,8 +281,19 @@ extern struct module __this_module;
__initcall_name(initcall, __iid, id), \
__initcall_section(__sec, __iid))
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+#define ___define_initcall(fn, id, __sec) \
+ __unique_initcall(fn, id, __sec, __initcall_id(fn)); \
+ static const struct builtin_initcall_record \
+ __initcall_name(builtin_initcall_rec, __initcall_id(fn), id) \
+ __used __aligned(8) __section(".rodata_builtin_initcall_records") = { \
+ .modname = KBUILD_MODNAME, \
+ .fnname = #fn, \
+ };
+#else
#define ___define_initcall(fn, id, __sec) \
__unique_initcall(fn, id, __sec, __initcall_id(fn))
+#endif
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
diff --git a/init/main.c b/init/main.c
index 96f93bb06c49..4cdf97306bf6 100644
--- a/init/main.c
+++ b/init/main.c
@@ -104,6 +104,8 @@
#include <linux/randomize_kstack.h>
#include <linux/pidfs.h>
#include <linux/ptdump.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/time_namespace.h>
#include <linux/unaligned.h>
#include <linux/vdso_datastore.h>
@@ -1251,7 +1253,7 @@ struct blacklist_entry {
char *buf;
};
-static __initdata_or_module LIST_HEAD(blacklisted_initcalls);
+static LIST_HEAD(blacklisted_initcalls);
static int __init initcall_blacklist(char *str)
{
@@ -1316,6 +1318,57 @@ static bool __init_or_module initcall_blacklisted(initcall_t fn)
#endif
__setup("initcall_blacklist=", initcall_blacklist);
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+extern struct builtin_initcall_record __builtin_initcall_records_start[];
+extern struct builtin_initcall_record __builtin_initcall_records_end[];
+
+static int builtin_initcalls_show(struct seq_file *m, void *v)
+{
+ struct builtin_initcall_record *rec;
+ bool blacklisted;
+ struct blacklist_entry *entry;
+
+ for (rec = __builtin_initcall_records_start; rec < __builtin_initcall_records_end; rec++) {
+ blacklisted = false;
+ list_for_each_entry(entry, &blacklisted_initcalls, next) {
+ if (!strcmp(rec->fnname, entry->buf)) {
+ blacklisted = true;
+ break;
+ }
+ }
+ seq_printf(m, "%s %s%s\n",
+ rec->modname,
+ rec->fnname,
+ blacklisted ? " [blacklisted]" : "");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(builtin_initcalls);
+
+static int __init builtin_initcalls_debugfs_init(void)
+{
+ struct dentry *modules_dir;
+
+ /* Attempt to safely grab a reference to the existing directory */
+ modules_dir = debugfs_lookup("modules", NULL);
+
+ /* * Use modules_dir if found, otherwise dynamically create and use
+ * the fallback directory without.
+ */
+ debugfs_create_file("builtin_initcalls", 0444,
+ modules_dir ?: debugfs_create_dir("modules", NULL),
+ NULL, &builtin_initcalls_fops);
+
+ /* dput() safely ignores NULL if the lookup failed */
+ dput(modules_dir);
+
+ return 0;
+}
+late_initcall(builtin_initcalls_debugfs_init);
+#endif /* CONFIG_DEBUG_FS && CONFIG_KALLSYMS */
+
+
static __init_or_module void
trace_initcall_start_cb(void *data, initcall_t fn)
{
--
2.51.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
2026-05-10 6:13 [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs Aaron Tomlin
@ 2026-05-11 20:34 ` Aaron Tomlin
2026-06-13 21:05 ` Aaron Tomlin
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Aaron Tomlin @ 2026-05-11 20:34 UTC (permalink / raw)
To: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen, sashal, leitao,
akpm
Cc: neelx, sean, chjohnst, steve, mproche, nick.lane, linux-arch,
linux-modules, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2091 bytes --]
On Sun, May 10, 2026 at 02:13:01AM -0400, Aaron Tomlin wrote:
> At present, identifying the correct function name to supply to the
> "initcall_blacklist=" kernel command-line parameter requires manual
> inspection of the source code or kernel symbol tables. Furthermore,
> administrators lack a reliable runtime mechanism to verify whether a
> specified built-in module has been successfully blacklisted.
>
> To resolve this, introduce a new debugfs interface at
> /sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
> built-in modules alongside their corresponding initialisation callbacks
> (e.g., those specified by module_init()) in a simple format:
> "module_name init_callback". If a built-in module has been actively
> blacklisted, the entry is explicitly appended with a " [blacklisted]"
> suffix.
>
> To achieve this without incurring runtime overhead, the
> ___define_initcall macro is expanded to statically allocate a
> builtin_initcall_record structure (containing the module and function
> name strings) within a dedicated .rodata_builtin_initcall_records
> section.
>
> This implementation incorporates several critical architectural safety
> measures:
> - The custom section name deliberately begins with ".rodata" to ensure
> KASLR (Kernel Address Space Layout Randomisation) relocation tools
> process and update the string pointers correctly during boot.
> - The structural alignment is explicitly forced to 8 bytes to prevent
> the compiler from inserting silent padding that would misalign the
> linker script boundaries.
> - The blacklisted_initcalls list head is stripped of its
> __initdata_or_module annotation, ensuring the list remains safely
> accessible in memory when queried from user space after early boot.
Hi Sasha, Breno,
I thought this might be of interest to you for review, given your recent
work and interest on the "killswitch" proposal [1].
[1]: https://lore.kernel.org/lkml/20260508195749.1885522-1-sashal@kernel.org/
Kind regards,
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
2026-05-10 6:13 [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs Aaron Tomlin
2026-05-11 20:34 ` Aaron Tomlin
@ 2026-06-13 21:05 ` Aaron Tomlin
2026-06-17 12:39 ` Petr Pavlu
2026-06-25 23:50 ` Andrew Morton
3 siblings, 0 replies; 6+ messages in thread
From: Aaron Tomlin @ 2026-06-13 21:05 UTC (permalink / raw)
To: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen
Cc: neelx, kees, peterz, akpm, sean, chjohnst, steve, mproche,
nick.lane, linux-arch, linux-modules, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1388 bytes --]
On Sun, May 10, 2026 at 02:13:01AM -0400, Aaron Tomlin wrote:
> At present, identifying the correct function name to supply to the
> "initcall_blacklist=" kernel command-line parameter requires manual
> inspection of the source code or kernel symbol tables. Furthermore,
> administrators lack a reliable runtime mechanism to verify whether a
> specified built-in module has been successfully blacklisted.
>
> To resolve this, introduce a new debugfs interface at
> /sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
> built-in modules alongside their corresponding initialisation callbacks
> (e.g., those specified by module_init()) in a simple format:
> "module_name init_callback". If a built-in module has been actively
> blacklisted, the entry is explicitly appended with a " [blacklisted]"
> suffix.
Dear maintainers,
I am writing to politely follow up on this patch, as it has been just over
a month since its initial submission.
To briefly reiterate, this patch introduces a reliable runtime mechanism to
identify built-in initcalls and verify their blacklisted status, thereby
significantly improving the usability of the "initcall_blacklist="
parameter.
I would be most grateful for any feedback, or to know whether any further
refinements are required for it to be considered for inclusion.
Kind regards,
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
2026-05-10 6:13 [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs Aaron Tomlin
2026-05-11 20:34 ` Aaron Tomlin
2026-06-13 21:05 ` Aaron Tomlin
@ 2026-06-17 12:39 ` Petr Pavlu
2026-06-20 23:11 ` Aaron Tomlin
2026-06-25 23:50 ` Andrew Morton
3 siblings, 1 reply; 6+ messages in thread
From: Petr Pavlu @ 2026-06-17 12:39 UTC (permalink / raw)
To: Aaron Tomlin
Cc: arnd, mcgrof, da.gomez, samitolvanen, neelx, sean, chjohnst,
steve, mproche, nick.lane, linux-arch, linux-modules,
linux-kernel
On 5/10/26 8:13 AM, Aaron Tomlin wrote:
> At present, identifying the correct function name to supply to the
> "initcall_blacklist=" kernel command-line parameter requires manual
> inspection of the source code or kernel symbol tables. Furthermore,
> administrators lack a reliable runtime mechanism to verify whether a
> specified built-in module has been successfully blacklisted.
My understanding is that initcall_blacklist is primarily a debugging
facility. This is documented in
Documentation/admin-guide/kernel-parameters.txt [1] and also outlined in
the initial commit 7b0b73d76651 ("init/main.c: add initcall_blacklist
kernel parameter") [2]. It is expected that to use it, one must inspect
the kernel source code.
If the goal is to allow specific built-in modules to be blacklisted,
I wonder whether extending module_blacklist to also cover built-in
modules might be a better option. Module names are more appropriate for
administrators to use, while initcall names should remain internal to
the kernel. Additionally, initcalls are typically "static" and therefore
are not guaranteed to have unique names + using module names avoids
a dependency on CONFIG_KALLSYMS=y.
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/kernel-parameters.txt?h=v7.1#n2408
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7b0b73d76651e5f88c88b76efa18d719f832bf6f
--
Thanks,
Petr
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
2026-06-17 12:39 ` Petr Pavlu
@ 2026-06-20 23:11 ` Aaron Tomlin
0 siblings, 0 replies; 6+ messages in thread
From: Aaron Tomlin @ 2026-06-20 23:11 UTC (permalink / raw)
To: Petr Pavlu
Cc: arnd, mcgrof, da.gomez, samitolvanen, neelx, sean, chjohnst,
steve, mproche, nick.lane, linux-arch, linux-modules,
linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2757 bytes --]
On Wed, Jun 17, 2026 at 02:39:53PM +0200, Petr Pavlu wrote:
> On 5/10/26 8:13 AM, Aaron Tomlin wrote:
> > At present, identifying the correct function name to supply to the
> > "initcall_blacklist=" kernel command-line parameter requires manual
> > inspection of the source code or kernel symbol tables. Furthermore,
> > administrators lack a reliable runtime mechanism to verify whether a
> > specified built-in module has been successfully blacklisted.
>
> My understanding is that initcall_blacklist is primarily a debugging
> facility. This is documented in
> Documentation/admin-guide/kernel-parameters.txt [1] and also outlined in
> the initial commit 7b0b73d76651 ("init/main.c: add initcall_blacklist
> kernel parameter") [2]. It is expected that to use it, one must inspect
> the kernel source code.
>
> If the goal is to allow specific built-in modules to be blacklisted,
> I wonder whether extending module_blacklist to also cover built-in
> modules might be a better option. Module names are more appropriate for
> administrators to use, while initcall names should remain internal to
> the kernel. Additionally, initcalls are typically "static" and therefore
> are not guaranteed to have unique names + using module names avoids
> a dependency on CONFIG_KALLSYMS=y.
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/kernel-parameters.txt?h=v7.1#n2408
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7b0b73d76651e5f88c88b76efa18d719f832bf6f
Hi Petr,
Thank you for your review and the excellent historical context.
Your points regarding static function name collisions and the
unnecessary kallsyms dependency are correct.
Extending the module blacklist parameter to intercept built in modules
makes far more architectural sense. It provides a consistent interface for
administrators and aligns perfectly with existing kernel conventions.
For the forthcoming v2 patchset, I have completely dropped the original
approach. Instead, I am implementing a parallel metadata section. By
updating the core initcall macros, the compiler will emit the module name
string into a custom ELF section directly alongside the function pointer.
The core boot code can then evaluate this mapping against the module
blacklist before executing the initialisation function.
This new architecture performs all the mapping at compile time. It entirely
avoids the kallsyms dependency you highlighted, perfectly preserves the
original function names for debugging purposes, and resides securely within
the initialisation data so the memory is freed completely immediately after
boot.
Kind regards,
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
2026-05-10 6:13 [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs Aaron Tomlin
` (2 preceding siblings ...)
2026-06-17 12:39 ` Petr Pavlu
@ 2026-06-25 23:50 ` Andrew Morton
3 siblings, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2026-06-25 23:50 UTC (permalink / raw)
To: Aaron Tomlin
Cc: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen, neelx, sean,
chjohnst, steve, mproche, nick.lane, linux-arch, linux-modules,
linux-kernel
On Sun, 10 May 2026 02:13:01 -0400 Aaron Tomlin <atomlin@atomlin.com> wrote:
> At present, identifying the correct function name to supply to the
> "initcall_blacklist=" kernel command-line parameter requires manual
> inspection of the source code or kernel symbol tables. Furthermore,
> administrators lack a reliable runtime mechanism to verify whether a
> specified built-in module has been successfully blacklisted.
I expect nobody has felt a need for this in their day-to-day work, so
the interest is low.
A detailed description of the use-cases would be of benefit - help
people better understand the value of this to our users. I suggest you
add this info and send a v2, if motivated.
> To resolve this, introduce a new debugfs interface at
> /sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
> built-in modules alongside their corresponding initialisation callbacks
> (e.g., those specified by module_init()) in a simple format:
> "module_name init_callback".
Isn't this information all available at build time?
Can we extract it and put it in some file somewhere which becomes part
of the distributed kernel package?
Or is it possible to write some userspace application which processes
the files which are typically in the kernel package and which extracts
this info?
> If a built-in module has been actively
> blacklisted, the entry is explicitly appended with a " [blacklisted]"
> suffix.
Can this be added to the /proc/modules output in some fashion? Perhaps
just add TAINT_BLACKLISTED and we get this info almost for free?
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-25 23:50 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-10 6:13 [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs Aaron Tomlin
2026-05-11 20:34 ` Aaron Tomlin
2026-06-13 21:05 ` Aaron Tomlin
2026-06-17 12:39 ` Petr Pavlu
2026-06-20 23:11 ` Aaron Tomlin
2026-06-25 23:50 ` Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox