From a7b4bc094fbaa7dc7b7b91ae33549bbd7eefaac1 Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Wed, 7 Jan 2026 13:22:57 +0100 Subject: module: Remove duplicate freeing of lockdep classes In the error path of load_module(), under the free_module label, the code calls lockdep_free_key_range() to release lock classes associated with the MOD_DATA, MOD_RODATA and MOD_RO_AFTER_INIT module regions, and subsequently invokes module_deallocate(). Since commit ac3b43283923 ("module: replace module_layout with module_memory"), the module_deallocate() function calls free_mod_mem(), which releases the lock classes as well and considers all module regions. Attempting to free these classes twice is unnecessary. Remove the redundant code in load_module(). Fixes: ac3b43283923 ("module: replace module_layout with module_memory") Signed-off-by: Petr Pavlu Reviewed-by: Daniel Gomez Reviewed-by: Aaron Tomlin Acked-by: Song Liu Acked-by: Peter Zijlstra (Intel) Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index 2bac4c7cd019..eec6c9cf8dbb 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3544,12 +3544,6 @@ static int load_module(struct load_info *info, const char __user *uargs, mutex_unlock(&module_mutex); free_module: mod_stat_bump_invalid(info, flags); - /* Free lock-classes; relies on the preceding sync_rcu() */ - for_class_mod_mem_type(type, core_data) { - lockdep_free_key_range(mod->mem[type].base, - mod->mem[type].size); - } - module_memory_restore_rox(mod); module_deallocate(mod, info); free_copy: -- cgit v1.2.3 From 8d597ba6ec18dae2eec143d4e1c9d81441ca0dda Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Thu, 5 Feb 2026 15:37:08 +0100 Subject: module: Fix the modversions and signing submenus The module Kconfig file contains a set of options related to "Module versioning support" (depends on MODVERSIONS) and "Module signature verification" (depends on MODULE_SIG). The Kconfig tool automatically creates submenus when an entry for a symbol is followed by consecutive items that all depend on the symbol. However, this functionality doesn't work for the mentioned module options. The MODVERSIONS options are interleaved with ASM_MODVERSIONS, which has no 'depends on MODVERSIONS' but instead uses 'default HAVE_ASM_MODVERSIONS && MODVERSIONS'. Similarly, the MODULE_SIG options are interleaved by a comment warning not to forget signing modules with scripts/sign-file, which uses the condition 'depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL'. The result is that the options are confusingly shown when using a menuconfig tool, as follows: [*] Module versioning support Module versioning implementation (genksyms (from source code)) ---> [ ] Extended Module Versioning Support [*] Basic Module Versioning Support [*] Source checksum for all modules [*] Module signature verification [ ] Require modules to be validly signed [ ] Automatically sign all modules Hash algorithm to sign modules (SHA-256) ---> Fix the issue by using if/endif to group related options together in kernel/module/Kconfig, similarly to how the MODULE_DEBUG options are already grouped. Note that the signing-related options depend on 'MODULE_SIG || IMA_APPRAISE_MODSIG', with the exception of MODULE_SIG_FORCE, which is valid only for MODULE_SIG and is therefore kept separately. For consistency, do the same for the MODULE_COMPRESS entries. The options are then properly placed into submenus, as follows: [*] Module versioning support Module versioning implementation (genksyms (from source code)) ---> [ ] Extended Module Versioning Support [*] Basic Module Versioning Support [*] Source checksum for all modules [*] Module signature verification [ ] Require modules to be validly signed [ ] Automatically sign all modules Hash algorithm to sign modules (SHA-256) ---> Signed-off-by: Petr Pavlu Reviewed-by: Daniel Gomez Signed-off-by: Sami Tolvanen --- kernel/module/Kconfig | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig index be74917802ad..43b1bb01fd27 100644 --- a/kernel/module/Kconfig +++ b/kernel/module/Kconfig @@ -169,9 +169,10 @@ config MODVERSIONS make them incompatible with the kernel you are running. If unsure, say N. +if MODVERSIONS + choice prompt "Module versioning implementation" - depends on MODVERSIONS help Select the tool used to calculate symbol versions for modules. @@ -206,7 +207,7 @@ endchoice config ASM_MODVERSIONS bool - default HAVE_ASM_MODVERSIONS && MODVERSIONS + default HAVE_ASM_MODVERSIONS help This enables module versioning for exported symbols also from assembly. This can be enabled only when the target architecture @@ -214,7 +215,6 @@ config ASM_MODVERSIONS config EXTENDED_MODVERSIONS bool "Extended Module Versioning Support" - depends on MODVERSIONS help This enables extended MODVERSIONs support, allowing long symbol names to be versioned. @@ -224,7 +224,6 @@ config EXTENDED_MODVERSIONS config BASIC_MODVERSIONS bool "Basic Module Versioning Support" - depends on MODVERSIONS default y help This enables basic MODVERSIONS support, allowing older tools or @@ -237,6 +236,8 @@ config BASIC_MODVERSIONS This is enabled by default when MODVERSIONS are enabled. If unsure, say Y. +endif # MODVERSIONS + config MODULE_SRCVERSION_ALL bool "Source checksum for all modules" help @@ -277,10 +278,11 @@ config MODULE_SIG_FORCE Reject unsigned modules or signed modules for which we don't have a key. Without this, such modules will simply taint the kernel. +if MODULE_SIG || IMA_APPRAISE_MODSIG + config MODULE_SIG_ALL bool "Automatically sign all modules" default y - depends on MODULE_SIG || IMA_APPRAISE_MODSIG help Sign all modules during make modules_install. Without this option, modules must be signed manually, using the scripts/sign-file tool. @@ -290,7 +292,6 @@ comment "Do not forget to sign required modules with scripts/sign-file" choice prompt "Hash algorithm to sign modules" - depends on MODULE_SIG || IMA_APPRAISE_MODSIG default MODULE_SIG_SHA512 help This determines which sort of hashing algorithm will be used during @@ -327,7 +328,6 @@ endchoice config MODULE_SIG_HASH string - depends on MODULE_SIG || IMA_APPRAISE_MODSIG default "sha256" if MODULE_SIG_SHA256 default "sha384" if MODULE_SIG_SHA384 default "sha512" if MODULE_SIG_SHA512 @@ -335,6 +335,8 @@ config MODULE_SIG_HASH default "sha3-384" if MODULE_SIG_SHA3_384 default "sha3-512" if MODULE_SIG_SHA3_512 +endif # MODULE_SIG || IMA_APPRAISE_MODSIG + config MODULE_COMPRESS bool "Module compression" help @@ -350,9 +352,10 @@ config MODULE_COMPRESS If unsure, say N. +if MODULE_COMPRESS + choice prompt "Module compression type" - depends on MODULE_COMPRESS help Choose the supported algorithm for module compression. @@ -379,7 +382,6 @@ endchoice config MODULE_COMPRESS_ALL bool "Automatically compress all modules" default y - depends on MODULE_COMPRESS help Compress all modules during 'make modules_install'. @@ -389,7 +391,6 @@ config MODULE_COMPRESS_ALL config MODULE_DECOMPRESS bool "Support in-kernel module decompression" - depends on MODULE_COMPRESS select ZLIB_INFLATE if MODULE_COMPRESS_GZIP select XZ_DEC if MODULE_COMPRESS_XZ select ZSTD_DECOMPRESS if MODULE_COMPRESS_ZSTD @@ -400,6 +401,8 @@ config MODULE_DECOMPRESS If unsure, say N. +endif # MODULE_COMPRESS + config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS bool "Allow loading of modules with missing namespace imports" help -- cgit v1.2.3 From f9d69d5e7bde2295eb7488a56f094ac8f5383b92 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Tue, 30 Dec 2025 10:32:08 -0800 Subject: module: Fix kernel panic when a symbol st_shndx is out of bounds The module loader doesn't check for bounds of the ELF section index in simplify_symbols(): for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { const char *name = info->strtab + sym[i].st_name; switch (sym[i].st_shndx) { case SHN_COMMON: [...] default: /* Divert to percpu allocation if a percpu var. */ if (sym[i].st_shndx == info->index.pcpu) secbase = (unsigned long)mod_percpu(mod); else /** HERE --> **/ secbase = info->sechdrs[sym[i].st_shndx].sh_addr; sym[i].st_value += secbase; break; } } A symbol with an out-of-bounds st_shndx value, for example 0xffff (known as SHN_XINDEX or SHN_HIRESERVE), may cause a kernel panic: BUG: unable to handle page fault for address: ... RIP: 0010:simplify_symbols+0x2b2/0x480 ... Kernel panic - not syncing: Fatal exception This can happen when module ELF is legitimately using SHN_XINDEX or when it is corrupted. Add a bounds check in simplify_symbols() to validate that st_shndx is within the valid range before using it. This issue was discovered due to a bug in llvm-objcopy, see relevant discussion for details [1]. [1] https://lore.kernel.org/linux-modules/20251224005752.201911-1-ihor.solodrai@linux.dev/ Signed-off-by: Ihor Solodrai Reviewed-by: Daniel Gomez Reviewed-by: Petr Pavlu Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index eec6c9cf8dbb..c3ce106c70af 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1568,6 +1568,13 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) break; default: + if (sym[i].st_shndx >= info->hdr->e_shnum) { + pr_err("%s: Symbol %s has an invalid section index %u (max %u)\n", + mod->name, name, sym[i].st_shndx, info->hdr->e_shnum - 1); + ret = -ENOEXEC; + break; + } + /* Divert to percpu allocation if a percpu var. */ if (sym[i].st_shndx == info->index.pcpu) secbase = (unsigned long)mod_percpu(mod); -- cgit v1.2.3 From deffe1edba626d474fef38007c03646ca5876a0e Mon Sep 17 00:00:00 2001 From: Petr Pavlu Date: Fri, 13 Mar 2026 14:48:02 +0100 Subject: module: Fix freeing of charp module parameters when CONFIG_SYSFS=n When setting a charp module parameter, the param_set_charp() function allocates memory to store a copy of the input value. Later, when the module is potentially unloaded, the destroy_params() function is called to free this allocated memory. However, destroy_params() is available only when CONFIG_SYSFS=y, otherwise only a dummy variant is present. In the unlikely case that the kernel is configured with CONFIG_MODULES=y and CONFIG_SYSFS=n, this results in a memory leak of charp values when a module is unloaded. Fix this issue by making destroy_params() always available when CONFIG_MODULES=y. Rename the function to module_destroy_params() to clarify that it is intended for use by the module loader. Fixes: e180a6b7759a ("param: fix charp parameters set via sysfs") Signed-off-by: Petr Pavlu Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index c3ce106c70af..ef2e2130972f 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1408,7 +1408,7 @@ static void free_module(struct module *mod) module_unload_free(mod); /* Free any allocated parameters. */ - destroy_params(mod->kp, mod->num_kp); + module_destroy_params(mod->kp, mod->num_kp); if (is_livepatch_module(mod)) free_module_elf(mod); @@ -3519,7 +3519,7 @@ static int load_module(struct load_info *info, const char __user *uargs, mod_sysfs_teardown(mod); coming_cleanup: mod->state = MODULE_STATE_GOING; - destroy_params(mod->kp, mod->num_kp); + module_destroy_params(mod->kp, mod->num_kp); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); klp_module_going(mod); -- cgit v1.2.3 From 3fe1dcbc2d20c5dbc581c0bb458e05365bfffcf7 Mon Sep 17 00:00:00 2001 From: Nicholas Sielicki Date: Sat, 7 Mar 2026 03:00:09 -0600 Subject: module: expose imported namespaces via sysfs Add /sys/module/*/import_ns to expose imported namespaces for currently loaded modules. The file contains one namespace per line and only exists for modules that import at least one namespace. Previously, the only way for userspace to inspect the symbol namespaces a module imports is to locate the .ko on disk and invoke modinfo(8) to decompress/parse the metadata. The kernel validated namespaces at load time, but it was otherwise discarded. Exposing this data via sysfs provides a runtime mechanism to verify which namespaces are being used by modules. For example, this allows userspace to audit driver API access in Android GKI, which uses symbol namespaces to restrict vendor drivers from using specific kernel interfaces (e.g., direct filesystem access). Signed-off-by: Nicholas Sielicki [Sami: Updated the commit message to explain motivation.] Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index ef2e2130972f..fc033137863d 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -607,6 +607,36 @@ static const struct module_attribute modinfo_##field = { \ MODINFO_ATTR(version); MODINFO_ATTR(srcversion); +static void setup_modinfo_import_ns(struct module *mod, const char *s) +{ + mod->imported_namespaces = NULL; +} + +static ssize_t show_modinfo_import_ns(const struct module_attribute *mattr, + struct module_kobject *mk, char *buffer) +{ + return sysfs_emit(buffer, "%s\n", mk->mod->imported_namespaces); +} + +static int modinfo_import_ns_exists(struct module *mod) +{ + return mod->imported_namespaces != NULL; +} + +static void free_modinfo_import_ns(struct module *mod) +{ + kfree(mod->imported_namespaces); + mod->imported_namespaces = NULL; +} + +static const struct module_attribute modinfo_import_ns = { + .attr = { .name = "import_ns", .mode = 0444 }, + .show = show_modinfo_import_ns, + .setup = setup_modinfo_import_ns, + .test = modinfo_import_ns_exists, + .free = free_modinfo_import_ns, +}; + static struct { char name[MODULE_NAME_LEN]; char taints[MODULE_FLAGS_BUF_SIZE]; @@ -1058,6 +1088,7 @@ const struct module_attribute *const modinfo_attrs[] = { &module_uevent, &modinfo_version, &modinfo_srcversion, + &modinfo_import_ns, &modinfo_initstate, &modinfo_coresize, #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC @@ -1760,11 +1791,43 @@ static void module_license_taint_check(struct module *mod, const char *license) } } +static int copy_modinfo_import_ns(struct module *mod, struct load_info *info) +{ + char *ns; + size_t len, total_len = 0; + char *buf, *p; + + for_each_modinfo_entry(ns, info, "import_ns") + total_len += strlen(ns) + 1; + + if (!total_len) { + mod->imported_namespaces = NULL; + return 0; + } + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + p = buf; + for_each_modinfo_entry(ns, info, "import_ns") { + len = strlen(ns); + memcpy(p, ns, len); + p += len; + *p++ = '\n'; + } + /* Replace trailing newline with null terminator. */ + *(p - 1) = '\0'; + + mod->imported_namespaces = buf; + return 0; +} + static int setup_modinfo(struct module *mod, struct load_info *info) { const struct module_attribute *attr; char *imported_namespace; - int i; + int i, err; for (i = 0; (attr = modinfo_attrs[i]); i++) { if (attr->setup) @@ -1783,6 +1846,10 @@ static int setup_modinfo(struct module *mod, struct load_info *info) } } + err = copy_modinfo_import_ns(mod, info); + if (err) + return err; + return 0; } -- cgit v1.2.3 From 2ae4ea2d9aaf25cb74fbc23450b1b8f0a5b7aa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 5 Mar 2026 10:31:40 +0100 Subject: module: Give MODULE_SIG_STRING a more descriptive name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose of the constant it is not entirely clear from its name. As this constant is going to be exposed in a UAPI header, give it a more specific name for clarity. As all its users call it 'marker', use that wording in the constant itself. Signed-off-by: Thomas Weißschuh Reviewed-by: Petr Pavlu Reviewed-by: Nicolas Schier Signed-off-by: Sami Tolvanen --- kernel/module/signing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/signing.c b/kernel/module/signing.c index a2ff4242e623..590ba29c85ab 100644 --- a/kernel/module/signing.c +++ b/kernel/module/signing.c @@ -70,7 +70,7 @@ int mod_verify_sig(const void *mod, struct load_info *info) int module_sig_check(struct load_info *info, int flags) { int err = -ENODATA; - const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; + const unsigned long markerlen = sizeof(MODULE_SIGNATURE_MARKER) - 1; const char *reason; const void *mod = info->hdr; bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS | @@ -81,7 +81,7 @@ int module_sig_check(struct load_info *info, int flags) */ if (!mangled_module && info->len > markerlen && - memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { + memcmp(mod + info->len - markerlen, MODULE_SIGNATURE_MARKER, markerlen) == 0) { /* We truncate the module to discard the signature */ info->len -= markerlen; err = mod_verify_sig(mod, info); -- cgit v1.2.3 From 55fcb926b6d8b5cfb40873e4840a69961db1bb69 Mon Sep 17 00:00:00 2001 From: Siddharth Nayyar Date: Thu, 26 Mar 2026 21:25:05 +0000 Subject: module: use kflagstab instead of *_gpl sections Read kflagstab section for vmlinux and modules to determine whether kernel symbols are GPL only. This patch eliminates the need for fragmenting the ksymtab for infering the value of GPL-only symbol flag, henceforth stop populating *_gpl versions of the ksymtab and kcrctab in modpost. Signed-off-by: Siddharth Nayyar Reviewed-by: Petr Pavlu Signed-off-by: Sami Tolvanen --- kernel/module/internal.h | 1 + kernel/module/main.c | 55 +++++++++++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 26 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/internal.h b/kernel/module/internal.h index 618202578b42..69b84510e097 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -57,6 +57,7 @@ extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const u32 __start___kcrctab[]; extern const u32 __start___kcrctab_gpl[]; +extern const u8 __start___kflagstab[]; #define KMOD_PATH_LEN 256 extern char modprobe_path[]; diff --git a/kernel/module/main.c b/kernel/module/main.c index fc033137863d..c243d6b79cdd 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ struct mod_tree_root mod_tree __cacheline_aligned = { struct symsearch { const struct kernel_symbol *start, *stop; const u32 *crcs; - enum mod_license license; + const u8 *flagstab; }; /* @@ -364,19 +365,21 @@ static bool find_exported_symbol_in_section(const struct symsearch *syms, struct find_symbol_arg *fsa) { struct kernel_symbol *sym; - - if (!fsa->gplok && syms->license == GPL_ONLY) - return false; + u8 sym_flags; sym = bsearch(fsa->name, syms->start, syms->stop - syms->start, sizeof(struct kernel_symbol), cmp_name); if (!sym) return false; + sym_flags = *(syms->flagstab + (sym - syms->start)); + if (!fsa->gplok && (sym_flags & KSYM_FLAG_GPL_ONLY)) + return false; + fsa->owner = owner; fsa->crc = symversion(syms->crcs, sym - syms->start); fsa->sym = sym; - fsa->license = syms->license; + fsa->license = (sym_flags & KSYM_FLAG_GPL_ONLY) ? GPL_ONLY : NOT_GPL_ONLY; return true; } @@ -387,36 +390,31 @@ static bool find_exported_symbol_in_section(const struct symsearch *syms, */ bool find_symbol(struct find_symbol_arg *fsa) { - static const struct symsearch arr[] = { - { __start___ksymtab, __stop___ksymtab, __start___kcrctab, - NOT_GPL_ONLY }, - { __start___ksymtab_gpl, __stop___ksymtab_gpl, - __start___kcrctab_gpl, - GPL_ONLY }, + const struct symsearch syms = { + .start = __start___ksymtab, + .stop = __stop___ksymtab, + .crcs = __start___kcrctab, + .flagstab = __start___kflagstab, }; struct module *mod; - unsigned int i; - for (i = 0; i < ARRAY_SIZE(arr); i++) - if (find_exported_symbol_in_section(&arr[i], NULL, fsa)) - return true; + if (find_exported_symbol_in_section(&syms, NULL, fsa)) + return true; list_for_each_entry_rcu(mod, &modules, list, lockdep_is_held(&module_mutex)) { - struct symsearch arr[] = { - { mod->syms, mod->syms + mod->num_syms, mod->crcs, - NOT_GPL_ONLY }, - { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, - mod->gpl_crcs, - GPL_ONLY }, + const struct symsearch syms = { + .start = mod->syms, + .stop = mod->syms + mod->num_syms, + .crcs = mod->crcs, + .flagstab = mod->flagstab, }; if (mod->state == MODULE_STATE_UNFORMED) continue; - for (i = 0; i < ARRAY_SIZE(arr); i++) - if (find_exported_symbol_in_section(&arr[i], mod, fsa)) - return true; + if (find_exported_symbol_in_section(&syms, mod, fsa)) + return true; } pr_debug("Failed to find symbol %s\n", fsa->name); @@ -2681,6 +2679,7 @@ static int find_module_sections(struct module *mod, struct load_info *info) sizeof(*mod->gpl_syms), &mod->num_gpl_syms); mod->gpl_crcs = section_addr(info, "__kcrctab_gpl"); + mod->flagstab = section_addr(info, "__kflagstab"); #ifdef CONFIG_CONSTRUCTORS mod->ctors = section_objs(info, ".ctors", @@ -2884,8 +2883,12 @@ out_err: return ret; } -static int check_export_symbol_versions(struct module *mod) +static int check_export_symbol_sections(struct module *mod) { + if (mod->num_syms && !mod->flagstab) { + pr_err("%s: no flags for exported symbols\n", mod->name); + return -ENOEXEC; + } #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !mod->crcs) || (mod->num_gpl_syms && !mod->gpl_crcs)) { @@ -3501,7 +3504,7 @@ static int load_module(struct load_info *info, const char __user *uargs, if (err) goto free_unload; - err = check_export_symbol_versions(mod); + err = check_export_symbol_sections(mod); if (err) goto free_unload; -- cgit v1.2.3 From b4760ff2a5e4351c59d185967735f59c0b0bd7f6 Mon Sep 17 00:00:00 2001 From: Siddharth Nayyar Date: Thu, 26 Mar 2026 21:25:06 +0000 Subject: module: deprecate usage of *_gpl sections in module loader The *_gpl section are not being used populated by modpost anymore. Hence the module loader doesn't need to find and process these sections in modules. This patch also simplifies symbol finding logic in module loader since *_gpl sections don't have to be searched anymore. Signed-off-by: Siddharth Nayyar Reviewed-by: Petr Pavlu Signed-off-by: Sami Tolvanen --- kernel/module/internal.h | 3 --- kernel/module/main.c | 46 ++++++++++++++++++---------------------------- 2 files changed, 18 insertions(+), 31 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/internal.h b/kernel/module/internal.h index 69b84510e097..061161cc79d9 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -53,10 +53,7 @@ extern const size_t modinfo_attrs_count; /* Provided by the linker */ extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; -extern const struct kernel_symbol __start___ksymtab_gpl[]; -extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const u32 __start___kcrctab[]; -extern const u32 __start___kcrctab_gpl[]; extern const u8 __start___kflagstab[]; #define KMOD_PATH_LEN 256 diff --git a/kernel/module/main.c b/kernel/module/main.c index c243d6b79cdd..c4f768953516 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1495,29 +1495,17 @@ EXPORT_SYMBOL_GPL(__symbol_get); */ static int verify_exported_symbols(struct module *mod) { - unsigned int i; const struct kernel_symbol *s; - struct { - const struct kernel_symbol *sym; - unsigned int num; - } arr[] = { - { mod->syms, mod->num_syms }, - { mod->gpl_syms, mod->num_gpl_syms }, - }; - - for (i = 0; i < ARRAY_SIZE(arr); i++) { - for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { - struct find_symbol_arg fsa = { - .name = kernel_symbol_name(s), - .gplok = true, - }; - if (find_symbol(&fsa)) { - pr_err("%s: exports duplicate symbol %s" - " (owned by %s)\n", - mod->name, kernel_symbol_name(s), - module_name(fsa.owner)); - return -ENOEXEC; - } + for (s = mod->syms; s < mod->syms + mod->num_syms; s++) { + struct find_symbol_arg fsa = { + .name = kernel_symbol_name(s), + .gplok = true, + }; + if (find_symbol(&fsa)) { + pr_err("%s: exports duplicate symbol %s (owned by %s)\n", + mod->name, kernel_symbol_name(s), + module_name(fsa.owner)); + return -ENOEXEC; } } return 0; @@ -2675,12 +2663,15 @@ static int find_module_sections(struct module *mod, struct load_info *info) mod->syms = section_objs(info, "__ksymtab", sizeof(*mod->syms), &mod->num_syms); mod->crcs = section_addr(info, "__kcrctab"); - mod->gpl_syms = section_objs(info, "__ksymtab_gpl", - sizeof(*mod->gpl_syms), - &mod->num_gpl_syms); - mod->gpl_crcs = section_addr(info, "__kcrctab_gpl"); mod->flagstab = section_addr(info, "__kflagstab"); + if (section_addr(info, "__ksymtab_gpl")) + pr_warn("%s: ignoring obsolete section __ksymtab_gpl\n", + mod->name); + if (section_addr(info, "__kcrctab_gpl")) + pr_warn("%s: ignoring obsolete section __kcrctab_gpl\n", + mod->name); + #ifdef CONFIG_CONSTRUCTORS mod->ctors = section_objs(info, ".ctors", sizeof(*mod->ctors), &mod->num_ctors); @@ -2890,8 +2881,7 @@ static int check_export_symbol_sections(struct module *mod) return -ENOEXEC; } #ifdef CONFIG_MODVERSIONS - if ((mod->num_syms && !mod->crcs) || - (mod->num_gpl_syms && !mod->gpl_crcs)) { + if (mod->num_syms && !mod->crcs) { return try_to_force_load(mod, "no versions for exported symbols"); } -- cgit v1.2.3 From 743f8cae549affe8eafb021b8c0e78a9f3bc23fa Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Mon, 30 Mar 2026 08:13:51 -0500 Subject: module: Override -EEXIST module return The -EEXIST errno is reserved by the module loading functionality. When userspace calls [f]init_module(), it expects a -EEXIST to mean that the module is already loaded in the kernel. If module_init() returns it, that is not true anymore. Override the error when returning to userspace: it doesn't make sense to change potentially long error propagation call chains just because it's will end up as the return of module_init(). Closes: https://lore.kernel.org/all/aKLzsAX14ybEjHfJ@orbyte.nwl.cc/ Cc: Greg Kroah-Hartman Cc: Aaron Tomlin Cc: Petr Pavlu Cc: Daniel Gomez Cc: Phil Sutter Cc: Christophe Leroy Signed-off-by: Lucas De Marchi [Sami: Fixed a typo.] Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index c4f768953516..ef6742536700 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3105,6 +3105,14 @@ static noinline int do_init_module(struct module *mod) if (mod->init != NULL) ret = do_one_initcall(mod->init); if (ret < 0) { + /* + * -EEXIST is reserved by [f]init_module() to signal to userspace that + * a module with this name is already loaded. Use something else if the + * module itself is returning that. + */ + if (ret == -EEXIST) + ret = -EBUSY; + goto fail_free_freeinit; } if (ret > 0) { -- cgit v1.2.3 From 663385f9155f27892a97a5824006f806a32eb8dc Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Mon, 30 Mar 2026 08:13:52 -0500 Subject: module: Simplify warning on positive returns from module_init() It should now be rare to trigger this warning - it doesn't need to be so verbose. Make it follow the usual style in the module loading code. For the same reason, drop the dump_stack(). Suggested-by: Petr Pavlu Signed-off-by: Lucas De Marchi Reviewed-by: Aaron Tomlin Reviewed-by: Petr Pavlu Reviewed-by: Daniel Gomez Signed-off-by: Sami Tolvanen --- kernel/module/main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'kernel/module') diff --git a/kernel/module/main.c b/kernel/module/main.c index ef6742536700..46dd8d25a605 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3115,13 +3115,9 @@ static noinline int do_init_module(struct module *mod) goto fail_free_freeinit; } - if (ret > 0) { - pr_warn("%s: '%s'->init suspiciously returned %d, it should " - "follow 0/-E convention\n" - "%s: loading module anyway...\n", - __func__, mod->name, ret, __func__); - dump_stack(); - } + if (ret > 0) + pr_warn("%s: init suspiciously returned %d, it should follow 0/-E convention\n", + mod->name, ret); /* Now it's a first class citizen! */ mod->state = MODULE_STATE_LIVE; -- cgit v1.2.3