Linux Modules
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Aaron Tomlin" <atomlin@atomlin.com>
Cc: linux-modules@vger.kernel.org
Subject: Re: [PATCH v2] module: Extend module_blacklist parameter to built-in modules
Date: Mon, 22 Jun 2026 14:17:47 +0000	[thread overview]
Message-ID: <20260622141748.0AB5D1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260622140259.2974-1-atomlin@atomlin.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] Accessing the .initcall.modnames array during loadable module initialization reads freed .init memory, causing a Use-After-Free.
- [High] Built-in module blacklisting silently bypasses or fails to compile under CONFIG_LTO_CLANG / CONFIG_CFI_CLANG due to function pointer mismatch.
- [Low] Kernel build fails when CONFIG_MODULES is disabled due to missing module_is_blacklisted() definition.
--

commit 5c29fcaa43d56c5e3cb5426f55d85a536519732f
Author: Aaron Tomlin <atomlin@atomlin.com>

module: Extend module_blacklist parameter to built-in modules

This patch extends the module_blacklist parameter to intercept and prevent
the initialization of built-in modules during system boot. It creates a
.initcall.modnames section mapping initcalls to module names, using 32-bit
relative offsets to preserve KASLR.

Link: https://lore.kernel.org/lkml/20260510061301.41341-1-atomlin@atomlin.com/

> diff --git a/include/linux/init.h b/include/linux/init.h
> index 40331923b9f4a..212f64a07c739 100644
> --- a/include/linux/init.h
> +++ b/include/linux/init.h

[ ... ]

> @@ -270,9 +280,26 @@ extern struct module __this_module;
>  		__initcall_stub(fn, __iid, id),			\
>  		__initcall_name(initcall, __iid, id),		\
>  		__initcall_section(__sec, __iid))
> +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
> +#define ___define_initcall(fn, id, __sec)				\
> +	__unique_initcall(fn, id, __sec, __initcall_id(fn));		\
> +	asm(".pushsection \".initcall.modnames\", \"a\"\n"		\
> +	    ".balign 4\n"						\
> +	    ".long " #fn " - .\n"					\

[Severity: High]
When compiled with CONFIG_LTO_CLANG or CONFIG_CFI_CLANG, the kernel wraps
initcalls in an __initcall_stub function, and stores the stub's address in
the initcall array.

Since ___define_initcall explicitly stores the original function's address
here, will initcall_get_modname() fail to match the runtime CFI/LTO stub
address passed by do_one_initcall()?

Also, could using #fn in inline assembly for a static function renamed by
LTO cause an undefined reference link error?

> +	    ".long __initcall_modstr_" #fn #id " - .\n"			\
> +	    ".popsection\n"						\
> +	    ".pushsection .init.rodata, \"a\"\n"			\
> +	    "__initcall_modstr_" #fn #id ": .string \"" KBUILD_MODNAME "\"\n" \
> +	    ".popsection\n");

[ ... ]

> diff --git a/include/linux/module.h b/include/linux/module.h
> index d80c3ea574726..21668ef1430a5 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -754,6 +754,8 @@ void *dereference_module_function_descriptor(struct module *mod, void *ptr);
>  int register_module_notifier(struct notifier_block *nb);
>  int unregister_module_notifier(struct notifier_block *nb);
>  
> +extern bool module_is_blacklisted(const char *module_name);

[Severity: Low]
Does this cause a build failure when CONFIG_MODULES is disabled?

The function module_is_blacklisted() is defined in kernel/module/main.c,
but it is declared here unconditionally and called from do_one_initcall()
in init/main.c. If CONFIG_MODULES is disabled, could the linker fail with
an undefined reference?

> +
>  extern void print_modules(void);

[ ... ]

> diff --git a/init/main.c b/init/main.c
> index b84818ad9685f..ce6d769b99e17 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -1365,12 +1365,39 @@ static inline void do_trace_initcall_level(const char *level)
>  }
>  #endif /* !TRACEPOINTS_ENABLED */
>  
> +extern struct initcall_modname __start_initcall_modnames[];
> +extern struct initcall_modname __stop_initcall_modnames[];
> +
> +static const char *initcall_get_modname(initcall_t fn)
> +{
> +	struct initcall_modname *p;
> +
> +	for (p = __start_initcall_modnames; p < __stop_initcall_modnames; p++) {
> +		if (initcall_from_entry(&p->initcall_fn) == fn) {
> +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
> +			return (const char *)offset_to_ptr(&p->modname_offset);
> +#else
> +			return p->modname;
> +#endif
> +		}
> +	}
> +	return NULL;
> +}
> +
>  int __init_or_module do_one_initcall(initcall_t fn)
>  {
>  	int count = preempt_count();
>  	char msgbuf[64];
> +	const char *modname;
>  	int ret;
>  
> +	modname = initcall_get_modname(fn);

[Severity: Critical]
Is there a use-after-free risk here when loading dynamic modules?

The .initcall.modnames section is placed in INIT_DATA_SECTION, mapping it to
.init.data. Memory in the .init.* sections is freed by free_initmem() after
the system boots.

Since do_one_initcall() is marked __init_or_module, it is also called by
do_init_module() whenever a loadable module is initialized after boot:

do_init_module()
  do_one_initcall()
    initcall_get_modname()

Does this unconditionally iterate over the __start_initcall_modnames array
residing in the freed .init.data section?

> +	if (modname && module_is_blacklisted(modname)) {
> +		pr_info("Skipping initcall for blacklisted built-in module %s\n",
> +			modname);
> +		return 0;
> +	}
> +
>  	if (initcall_blacklisted(fn))
>  		return -EPERM;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622140259.2974-1-atomlin@atomlin.com?part=1

      reply	other threads:[~2026-06-22 14:17 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-22 14:02 [PATCH v2] module: Extend module_blacklist parameter to built-in modules Aaron Tomlin
2026-06-22 14:17 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260622141748.0AB5D1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=atomlin@atomlin.com \
    --cc=linux-modules@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox