Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

modules: Support extended MODVERSIONS info

Adds a new format for MODVERSIONS which stores each field in a separate
ELF section. This initially adds support for variable length names, but
could later be used to add additional fields to MODVERSIONS in a
backwards compatible way if needed. Any new fields will be ignored by
old user tooling, unlike the current format where user tooling cannot
tolerate adjustments to the format (for example making the name field
longer).

Since PPC munges its version records to strip leading dots, we reproduce
the munging for the new format. Other architectures do not appear to
have architecture-specific usage of this information.

Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Matthew Maurer <mmaurer@google.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Matthew Maurer and committed by
Masahiro Yamada
54ac1ac8 8c6d7b41

+162 -10
+22 -2
arch/powerpc/kernel/module_64.c
··· 369 369 } 370 370 } 371 371 372 + /* Same as normal versions, remove a leading dot if present. */ 373 + static void dedotify_ext_version_names(char *str_seq, unsigned long size) 374 + { 375 + unsigned long out = 0; 376 + unsigned long in; 377 + char last = '\0'; 378 + 379 + for (in = 0; in < size; in++) { 380 + /* Skip one leading dot */ 381 + if (last == '\0' && str_seq[in] == '.') 382 + in++; 383 + last = str_seq[in]; 384 + str_seq[out++] = last; 385 + } 386 + /* Zero the trailing portion of the names table for robustness */ 387 + memset(&str_seq[out], 0, size - out); 388 + } 389 + 372 390 /* 373 391 * Undefined symbols which refer to .funcname, hack to funcname. Make .TOC. 374 392 * seem to be defined (value set later). ··· 456 438 me->arch.toc_section = i; 457 439 if (sechdrs[i].sh_addralign < 8) 458 440 sechdrs[i].sh_addralign = 8; 459 - } 460 - else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0) 441 + } else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") == 0) 461 442 dedotify_versions((void *)hdr + sechdrs[i].sh_offset, 462 443 sechdrs[i].sh_size); 444 + else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") == 0) 445 + dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset, 446 + sechdrs[i].sh_size); 463 447 464 448 if (sechdrs[i].sh_type == SHT_SYMTAB) 465 449 dedotify((void *)hdr + sechdrs[i].sh_offset,
+11
kernel/module/internal.h
··· 86 86 unsigned int vers; 87 87 unsigned int info; 88 88 unsigned int pcpu; 89 + unsigned int vers_ext_crc; 90 + unsigned int vers_ext_name; 89 91 } index; 90 92 }; 91 93 ··· 391 389 struct kernel_symbol *ks, struct tracepoint * const *tp); 392 390 int check_modstruct_version(const struct load_info *info, struct module *mod); 393 391 int same_magic(const char *amagic, const char *bmagic, bool has_crcs); 392 + struct modversion_info_ext { 393 + size_t remaining; 394 + const u32 *crc; 395 + const char *name; 396 + }; 397 + void modversion_ext_start(const struct load_info *info, struct modversion_info_ext *ver); 398 + void modversion_ext_advance(struct modversion_info_ext *ver); 399 + #define for_each_modversion_info_ext(ver, info) \ 400 + for (modversion_ext_start(info, &ver); ver.remaining > 0; modversion_ext_advance(&ver)) 394 401 #else /* !CONFIG_MODVERSIONS */ 395 402 static inline int check_version(const struct load_info *info, 396 403 const char *symname,
+84 -8
kernel/module/main.c
··· 2074 2074 } 2075 2075 2076 2076 /** 2077 + * elf_validity_cache_index_versions() - Validate and cache version indices 2078 + * @info: Load info to cache version indices in. 2079 + * Must have &load_info->sechdrs and &load_info->secstrings populated. 2080 + * @flags: Load flags, relevant to suppress version loading, see 2081 + * uapi/linux/module.h 2082 + * 2083 + * If we're ignoring modversions based on @flags, zero all version indices 2084 + * and return validity. Othewrise check: 2085 + * 2086 + * * If "__version_ext_crcs" is present, "__version_ext_names" is present 2087 + * * There is a name present for every crc 2088 + * 2089 + * Then populate: 2090 + * 2091 + * * &load_info->index.vers 2092 + * * &load_info->index.vers_ext_crc 2093 + * * &load_info->index.vers_ext_names 2094 + * 2095 + * if present. 2096 + * 2097 + * Return: %0 if valid, %-ENOEXEC on failure. 2098 + */ 2099 + static int elf_validity_cache_index_versions(struct load_info *info, int flags) 2100 + { 2101 + unsigned int vers_ext_crc; 2102 + unsigned int vers_ext_name; 2103 + size_t crc_count; 2104 + size_t remaining_len; 2105 + size_t name_size; 2106 + char *name; 2107 + 2108 + /* If modversions were suppressed, pretend we didn't find any */ 2109 + if (flags & MODULE_INIT_IGNORE_MODVERSIONS) { 2110 + info->index.vers = 0; 2111 + info->index.vers_ext_crc = 0; 2112 + info->index.vers_ext_name = 0; 2113 + return 0; 2114 + } 2115 + 2116 + vers_ext_crc = find_sec(info, "__version_ext_crcs"); 2117 + vers_ext_name = find_sec(info, "__version_ext_names"); 2118 + 2119 + /* If we have one field, we must have the other */ 2120 + if (!!vers_ext_crc != !!vers_ext_name) { 2121 + pr_err("extended version crc+name presence does not match"); 2122 + return -ENOEXEC; 2123 + } 2124 + 2125 + /* 2126 + * If we have extended version information, we should have the same 2127 + * number of entries in every section. 2128 + */ 2129 + if (vers_ext_crc) { 2130 + crc_count = info->sechdrs[vers_ext_crc].sh_size / sizeof(u32); 2131 + name = (void *)info->hdr + 2132 + info->sechdrs[vers_ext_name].sh_offset; 2133 + remaining_len = info->sechdrs[vers_ext_name].sh_size; 2134 + 2135 + while (crc_count--) { 2136 + name_size = strnlen(name, remaining_len) + 1; 2137 + if (name_size > remaining_len) { 2138 + pr_err("more extended version crcs than names"); 2139 + return -ENOEXEC; 2140 + } 2141 + remaining_len -= name_size; 2142 + name += name_size; 2143 + } 2144 + } 2145 + 2146 + info->index.vers = find_sec(info, "__versions"); 2147 + info->index.vers_ext_crc = vers_ext_crc; 2148 + info->index.vers_ext_name = vers_ext_name; 2149 + return 0; 2150 + } 2151 + 2152 + /** 2077 2153 * elf_validity_cache_index() - Resolve, validate, cache section indices 2078 2154 * @info: Load info to read from and update. 2079 2155 * &load_info->sechdrs and &load_info->secstrings must be populated. ··· 2163 2087 * * elf_validity_cache_index_mod() 2164 2088 * * elf_validity_cache_index_sym() 2165 2089 * * elf_validity_cache_index_str() 2166 - * 2167 - * If versioning is not suppressed via flags, load the version index from 2168 - * a section called "__versions" with no validation. 2090 + * * elf_validity_cache_index_versions() 2169 2091 * 2170 2092 * If CONFIG_SMP is enabled, load the percpu section by name with no 2171 2093 * validation. ··· 2186 2112 err = elf_validity_cache_index_str(info); 2187 2113 if (err < 0) 2188 2114 return err; 2189 - 2190 - if (flags & MODULE_INIT_IGNORE_MODVERSIONS) 2191 - info->index.vers = 0; /* Pretend no __versions section! */ 2192 - else 2193 - info->index.vers = find_sec(info, "__versions"); 2115 + err = elf_validity_cache_index_versions(info, flags); 2116 + if (err < 0) 2117 + return err; 2194 2118 2195 2119 info->index.pcpu = find_pcpusec(info); 2196 2120 ··· 2399 2327 2400 2328 /* Track but don't keep modinfo and version sections. */ 2401 2329 info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC; 2330 + info->sechdrs[info->index.vers_ext_crc].sh_flags &= 2331 + ~(unsigned long)SHF_ALLOC; 2332 + info->sechdrs[info->index.vers_ext_name].sh_flags &= 2333 + ~(unsigned long)SHF_ALLOC; 2402 2334 info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC; 2403 2335 2404 2336 return 0;
+45
kernel/module/version.c
··· 19 19 unsigned int versindex = info->index.vers; 20 20 unsigned int i, num_versions; 21 21 struct modversion_info *versions; 22 + struct modversion_info_ext version_ext; 22 23 23 24 /* Exporting module didn't supply crcs? OK, we're already tainted. */ 24 25 if (!crc) 25 26 return 1; 27 + 28 + /* If we have extended version info, rely on it */ 29 + if (info->index.vers_ext_crc) { 30 + for_each_modversion_info_ext(version_ext, info) { 31 + if (strcmp(version_ext.name, symname) != 0) 32 + continue; 33 + if (*version_ext.crc == *crc) 34 + return 1; 35 + pr_debug("Found checksum %X vs module %X\n", 36 + *crc, *version_ext.crc); 37 + goto bad_version; 38 + } 39 + pr_warn_once("%s: no extended symbol version for %s\n", 40 + info->name, symname); 41 + return 1; 42 + } 26 43 27 44 /* No versions at all? modprobe --force does this. */ 28 45 if (versindex == 0) ··· 102 85 bmagic += strcspn(bmagic, " "); 103 86 } 104 87 return strcmp(amagic, bmagic) == 0; 88 + } 89 + 90 + void modversion_ext_start(const struct load_info *info, 91 + struct modversion_info_ext *start) 92 + { 93 + unsigned int crc_idx = info->index.vers_ext_crc; 94 + unsigned int name_idx = info->index.vers_ext_name; 95 + Elf_Shdr *sechdrs = info->sechdrs; 96 + 97 + /* 98 + * Both of these fields are needed for this to be useful 99 + * Any future fields should be initialized to NULL if absent. 100 + */ 101 + if (crc_idx == 0 || name_idx == 0) { 102 + start->remaining = 0; 103 + return; 104 + } 105 + 106 + start->crc = (const u32 *)sechdrs[crc_idx].sh_addr; 107 + start->name = (const char *)sechdrs[name_idx].sh_addr; 108 + start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc); 109 + } 110 + 111 + void modversion_ext_advance(struct modversion_info_ext *vers) 112 + { 113 + vers->remaining--; 114 + vers->crc++; 115 + vers->name += strlen(vers->name) + 1; 105 116 } 106 117 107 118 /*