| // 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); |