mirror of
https://github.com/torvalds/linux.git
synced 2025-04-09 14:45:27 +00:00

module_assert_mutex_or_preempt() is not needed in find_symbol(). The function checks for RCU-sched or the module_mutex to be acquired. The list_for_each_entry_rcu() below does the same check. Remove module_assert_mutex_or_preempt() from try_add_tainted_module(). Use RCU protection to invoke find_symbol() and update callers. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20250108090457.512198-11-bigeasy@linutronix.de Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
147 lines
3.6 KiB
C
147 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Module version support
|
|
*
|
|
* Copyright (C) 2008 Rusty Russell
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/string.h>
|
|
#include <linux/printk.h>
|
|
#include "internal.h"
|
|
|
|
int check_version(const struct load_info *info,
|
|
const char *symname,
|
|
struct module *mod,
|
|
const u32 *crc)
|
|
{
|
|
Elf_Shdr *sechdrs = info->sechdrs;
|
|
unsigned int versindex = info->index.vers;
|
|
unsigned int i, num_versions;
|
|
struct modversion_info *versions;
|
|
struct modversion_info_ext version_ext;
|
|
|
|
/* Exporting module didn't supply crcs? OK, we're already tainted. */
|
|
if (!crc)
|
|
return 1;
|
|
|
|
/* If we have extended version info, rely on it */
|
|
if (info->index.vers_ext_crc) {
|
|
for_each_modversion_info_ext(version_ext, info) {
|
|
if (strcmp(version_ext.name, symname) != 0)
|
|
continue;
|
|
if (*version_ext.crc == *crc)
|
|
return 1;
|
|
pr_debug("Found checksum %X vs module %X\n",
|
|
*crc, *version_ext.crc);
|
|
goto bad_version;
|
|
}
|
|
pr_warn_once("%s: no extended symbol version for %s\n",
|
|
info->name, symname);
|
|
return 1;
|
|
}
|
|
|
|
/* No versions at all? modprobe --force does this. */
|
|
if (versindex == 0)
|
|
return try_to_force_load(mod, symname) == 0;
|
|
|
|
versions = (void *)sechdrs[versindex].sh_addr;
|
|
num_versions = sechdrs[versindex].sh_size
|
|
/ sizeof(struct modversion_info);
|
|
|
|
for (i = 0; i < num_versions; i++) {
|
|
u32 crcval;
|
|
|
|
if (strcmp(versions[i].name, symname) != 0)
|
|
continue;
|
|
|
|
crcval = *crc;
|
|
if (versions[i].crc == crcval)
|
|
return 1;
|
|
pr_debug("Found checksum %X vs module %lX\n",
|
|
crcval, versions[i].crc);
|
|
goto bad_version;
|
|
}
|
|
|
|
/* Broken toolchain. Warn once, then let it go.. */
|
|
pr_warn_once("%s: no symbol version for %s\n", info->name, symname);
|
|
return 1;
|
|
|
|
bad_version:
|
|
pr_warn("%s: disagrees about version of symbol %s\n", info->name, symname);
|
|
return 0;
|
|
}
|
|
|
|
int check_modstruct_version(const struct load_info *info,
|
|
struct module *mod)
|
|
{
|
|
struct find_symbol_arg fsa = {
|
|
.name = "module_layout",
|
|
.gplok = true,
|
|
};
|
|
bool have_symbol;
|
|
|
|
/*
|
|
* Since this should be found in kernel (which can't be removed), no
|
|
* locking is necessary. Regardless use a RCU read section to keep
|
|
* lockdep happy.
|
|
*/
|
|
scoped_guard(rcu)
|
|
have_symbol = find_symbol(&fsa);
|
|
BUG_ON(!have_symbol);
|
|
|
|
return check_version(info, "module_layout", mod, fsa.crc);
|
|
}
|
|
|
|
/* First part is kernel version, which we ignore if module has crcs. */
|
|
int same_magic(const char *amagic, const char *bmagic,
|
|
bool has_crcs)
|
|
{
|
|
if (has_crcs) {
|
|
amagic += strcspn(amagic, " ");
|
|
bmagic += strcspn(bmagic, " ");
|
|
}
|
|
return strcmp(amagic, bmagic) == 0;
|
|
}
|
|
|
|
void modversion_ext_start(const struct load_info *info,
|
|
struct modversion_info_ext *start)
|
|
{
|
|
unsigned int crc_idx = info->index.vers_ext_crc;
|
|
unsigned int name_idx = info->index.vers_ext_name;
|
|
Elf_Shdr *sechdrs = info->sechdrs;
|
|
|
|
/*
|
|
* Both of these fields are needed for this to be useful
|
|
* Any future fields should be initialized to NULL if absent.
|
|
*/
|
|
if (crc_idx == 0 || name_idx == 0) {
|
|
start->remaining = 0;
|
|
return;
|
|
}
|
|
|
|
start->crc = (const u32 *)sechdrs[crc_idx].sh_addr;
|
|
start->name = (const char *)sechdrs[name_idx].sh_addr;
|
|
start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
|
|
}
|
|
|
|
void modversion_ext_advance(struct modversion_info_ext *vers)
|
|
{
|
|
vers->remaining--;
|
|
vers->crc++;
|
|
vers->name += strlen(vers->name) + 1;
|
|
}
|
|
|
|
/*
|
|
* Generate the signature for all relevant module structures here.
|
|
* If these change, we don't want to try to parse the module.
|
|
*/
|
|
void module_layout(struct module *mod,
|
|
struct modversion_info *ver,
|
|
struct kernel_param *kp,
|
|
struct kernel_symbol *ks,
|
|
struct tracepoint * const *tp)
|
|
{
|
|
}
|
|
EXPORT_SYMBOL(module_layout);
|