include/linux/module.h | 9 +++------ kernel/Makefile | 2 +- kernel/bpf/verifier.c | 2 -- kernel/module/Makefile | 4 ++++ kernel/module/main.c | 1 + kernel/module/stubs.c | 11 +++++++++++ 6 files changed, 20 insertions(+), 9 deletions(-)
Module-related stubs for CONFIG_MODULES=n can normally live in
linux/module.h. This works well when the stub does nothing or simply
returns a constant. However, some non-module implementations may be more
expensive, either because they have a real implementation or because they
require additional dependencies to be pulled into linux/module.h. This
header appears in roughly 15k #include directives across the kernel, so it
should avoid bringing in unnecessary definitions.
Specifically, linux/module.h defines module_put_and_kthread_exit() as an
alias for kthread_exit() when CONFIG_MODULES=n. That in turn requires
linux/kthread.h, even though linux/module.h otherwise has no need for it.
Note that linux/module.h currently doesn't include linux/kthread.h and
requires its users to pick it up through other paths, which is itself not
quite right.
Introduce kernel/module/stubs.c to hold such stubs, built only for
CONFIG_MODULES=n. This mirrors kernel/time/posix-stubs.c, which is used for
CONFIG_POSIX_TIMERS=n.
Define module_put_and_kthread_exit() in both CONFIG_MODULES configurations
to call __module_put_and_kthread_exit() and add a CONFIG_MODULES=n
implementation of that function to kernel/module/stubs.c. Since
__module_put_and_kthread_exit() is now implemented even when
CONFIG_MODULES=n, remove the corresponding '#ifdef CONFIG_MODULES' from the
noreturn_deny BPF list.
Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
---
kernel/module/stubs.c should also be used for add_taint_module() [1] in the
future.
[1] https://lore.kernel.org/linux-modules/a79a6a06-cc48-4036-882d-95e9b4cc01cf@suse.com/
---
include/linux/module.h | 9 +++------
kernel/Makefile | 2 +-
kernel/bpf/verifier.c | 2 --
kernel/module/Makefile | 4 ++++
kernel/module/main.c | 1 +
kernel/module/stubs.c | 11 +++++++++++
6 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/include/linux/module.h b/include/linux/module.h
index 96cc98568eea..4c7bd7f9f889 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -658,10 +658,6 @@ static inline bool within_module(unsigned long addr, const struct module *mod)
/* Search for module by name: must be in a RCU critical section. */
struct module *find_module(const char *name);
-extern void __noreturn __module_put_and_kthread_exit(struct module *mod,
- long code);
-#define module_put_and_kthread_exit(code) __module_put_and_kthread_exit(THIS_MODULE, code)
-
#ifdef CONFIG_MODULE_UNLOAD
int module_refcount(struct module *mod);
void __symbol_put(const char *symbol);
@@ -850,8 +846,6 @@ static inline int unregister_module_notifier(struct notifier_block *nb)
return 0;
}
-#define module_put_and_kthread_exit(code) kthread_exit(code)
-
static inline void print_modules(void)
{
}
@@ -879,6 +873,9 @@ static inline void module_for_each_mod(int(*func)(struct module *mod, void *data
}
#endif /* CONFIG_MODULES */
+void __noreturn __module_put_and_kthread_exit(struct module *mod, long code);
+#define module_put_and_kthread_exit(code) __module_put_and_kthread_exit(THIS_MODULE, code)
+
#ifdef CONFIG_SYSFS
extern struct kset *module_kset;
extern const struct kobj_type module_ktype;
diff --git a/kernel/Makefile b/kernel/Makefile
index 1e1a31673577..08a94594975d 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -58,7 +58,7 @@ obj-y += liveupdate/
obj-y += dma/
obj-y += entry/
obj-y += unwind/
-obj-$(CONFIG_MODULES) += module/
+obj-y += module/
obj-$(CONFIG_KCMP) += kcmp.o
obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 72a3f5998dd2..be70f10e4968 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20417,9 +20417,7 @@ BTF_ID(func, __ia32_sys_exit_group)
BTF_ID(func, __kunit_abort)
BTF_ID(func, kunit_try_catch_throw)
#endif
-#ifdef CONFIG_MODULES
BTF_ID(func, __module_put_and_kthread_exit)
-#endif
#ifdef CONFIG_X86_64
BTF_ID(func, __x64_sys_exit)
BTF_ID(func, __x64_sys_exit_group)
diff --git a/kernel/module/Makefile b/kernel/module/Makefile
index 50ffcc413b54..72fd2972eacf 100644
--- a/kernel/module/Makefile
+++ b/kernel/module/Makefile
@@ -7,6 +7,7 @@
# and produce insane amounts of uninteresting coverage.
KCOV_INSTRUMENT_main.o := n
+ifeq ($(CONFIG_MODULES),y)
obj-y += main.o
obj-y += strict_rwx.o
obj-y += kmod.o
@@ -23,3 +24,6 @@ obj-$(CONFIG_KGDB_KDB) += kdb.o
obj-$(CONFIG_MODVERSIONS) += version.o
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
obj-$(CONFIG_MODULE_STATS) += stats.o
+else
+obj-y += stubs.o
+endif
diff --git a/kernel/module/main.c b/kernel/module/main.c
index d0e1e0bd2ad0..a8bf803afa81 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -60,6 +60,7 @@
#include <linux/codetag.h>
#include <linux/debugfs.h>
#include <linux/execmem.h>
+#include <linux/kthread.h>
#include <uapi/linux/module.h>
#include "internal.h"
diff --git a/kernel/module/stubs.c b/kernel/module/stubs.c
new file mode 100644
index 000000000000..81e66c02260c
--- /dev/null
+++ b/kernel/module/stubs.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include "internal.h"
+
+void __noreturn __module_put_and_kthread_exit(struct module *mod, long code)
+{
+ kthread_exit(code);
+}
+EXPORT_SYMBOL(__module_put_and_kthread_exit);
---
base-commit: 93f51579e7df248780214094418f205253383cc5
change-id: 20260918-module-stubs-dccefe41dace
> diff --git a/include/linux/module.h b/include/linux/module.h
> index 96cc98568eea5..4c7bd7f9f8898 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
[ ... ]
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 1e1a31673577d..08a94594975d2 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index d62c0f74cff5e..193610e20a4a8 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> diff --git a/kernel/module/Makefile b/kernel/module/Makefile
> index 50ffcc413b545..72fd2972eacfd 100644
> --- a/kernel/module/Makefile
> +++ b/kernel/module/Makefile
[ ... ]
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index d0e1e0bd2ad06..a8bf803afa813 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
[ ... ]
> diff --git a/kernel/module/stubs.c b/kernel/module/stubs.c
> new file mode 100644
> index 0000000000000..81e66c02260c8
> --- /dev/null
> +++ b/kernel/module/stubs.c
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/kthread.h>
> +#include <linux/module.h>
> +#include "internal.h"
This isn't a bug, but does stubs.c need "internal.h" here, or would
<linux/kthread.h> plus <linux/module.h> be enough?
kthread_exit() comes from <linux/kthread.h>, and struct module,
__noreturn, and EXPORT_SYMBOL come from <linux/module.h>. The file
doesn't appear to use anything that internal.h provides.
This makes stubs.c the only consumer of that header outside
CONFIG_MODULES=y, which future internal.h changes would have to keep
working.
> +
> +void __noreturn __module_put_and_kthread_exit(struct module *mod, long code)
> +{
> + kthread_exit(code);
> +}
> +EXPORT_SYMBOL(__module_put_and_kthread_exit);
This isn't a bug, but since stubs.o only exists for CONFIG_MODULES=n, is
the EXPORT_SYMBOL() reachable by any module, or can it be dropped?
No loadable module can resolve this export when CONFIG_MODULES=n. The
macro still expands (producing an .export_symbol entry that
COMMON_DISCARDS drops at link, plus __ADDRESSABLE()), so it's harmless
but has no consumer.
kernel/time/posix-stubs.c, cited in the commit message as the model,
exports nothing.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/35733170542
© 2016 - 2026 Red Hat, Inc.