1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// 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;
}
pr_warn("%s: no symbol version for %s\n", info->name, symname);
return 0;
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);
|