Skip to content

Commit 54ac1ac

Browse files
maurermasahir0y
authored andcommitted
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>
1 parent 8c6d7b4 commit 54ac1ac

4 files changed

Lines changed: 162 additions & 10 deletions

File tree

arch/powerpc/kernel/module_64.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,24 @@ static void dedotify_versions(struct modversion_info *vers,
369369
}
370370
}
371371

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+
372390
/*
373391
* Undefined symbols which refer to .funcname, hack to funcname. Make .TOC.
374392
* seem to be defined (value set later).
@@ -438,10 +456,12 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
438456
me->arch.toc_section = i;
439457
if (sechdrs[i].sh_addralign < 8)
440458
sechdrs[i].sh_addralign = 8;
441-
}
442-
else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
459+
} else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") == 0)
443460
dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
444461
sechdrs[i].sh_size);
462+
else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") == 0)
463+
dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset,
464+
sechdrs[i].sh_size);
445465

446466
if (sechdrs[i].sh_type == SHT_SYMTAB)
447467
dedotify((void *)hdr + sechdrs[i].sh_offset,

kernel/module/internal.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ struct load_info {
8686
unsigned int vers;
8787
unsigned int info;
8888
unsigned int pcpu;
89+
unsigned int vers_ext_crc;
90+
unsigned int vers_ext_name;
8991
} index;
9092
};
9193

@@ -389,6 +391,15 @@ void module_layout(struct module *mod, struct modversion_info *ver, struct kerne
389391
struct kernel_symbol *ks, struct tracepoint * const *tp);
390392
int check_modstruct_version(const struct load_info *info, struct module *mod);
391393
int same_magic(const char *amagic, const char *bmagic, bool has_crcs);
394+
struct modversion_info_ext {
395+
size_t remaining;
396+
const u32 *crc;
397+
const char *name;
398+
};
399+
void modversion_ext_start(const struct load_info *info, struct modversion_info_ext *ver);
400+
void modversion_ext_advance(struct modversion_info_ext *ver);
401+
#define for_each_modversion_info_ext(ver, info) \
402+
for (modversion_ext_start(info, &ver); ver.remaining > 0; modversion_ext_advance(&ver))
392403
#else /* !CONFIG_MODVERSIONS */
393404
static inline int check_version(const struct load_info *info,
394405
const char *symname,

kernel/module/main.c

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,6 +2073,82 @@ static int elf_validity_cache_index_str(struct load_info *info)
20732073
return 0;
20742074
}
20752075

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+
20762152
/**
20772153
* elf_validity_cache_index() - Resolve, validate, cache section indices
20782154
* @info: Load info to read from and update.
@@ -2087,9 +2163,7 @@ static int elf_validity_cache_index_str(struct load_info *info)
20872163
* * elf_validity_cache_index_mod()
20882164
* * elf_validity_cache_index_sym()
20892165
* * elf_validity_cache_index_str()
2090-
*
2091-
* If versioning is not suppressed via flags, load the version index from
2092-
* a section called "__versions" with no validation.
2166+
* * elf_validity_cache_index_versions()
20932167
*
20942168
* If CONFIG_SMP is enabled, load the percpu section by name with no
20952169
* validation.
@@ -2112,11 +2186,9 @@ static int elf_validity_cache_index(struct load_info *info, int flags)
21122186
err = elf_validity_cache_index_str(info);
21132187
if (err < 0)
21142188
return err;
2115-
2116-
if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
2117-
info->index.vers = 0; /* Pretend no __versions section! */
2118-
else
2119-
info->index.vers = find_sec(info, "__versions");
2189+
err = elf_validity_cache_index_versions(info, flags);
2190+
if (err < 0)
2191+
return err;
21202192

21212193
info->index.pcpu = find_pcpusec(info);
21222194

@@ -2327,6 +2399,10 @@ static int rewrite_section_headers(struct load_info *info, int flags)
23272399

23282400
/* Track but don't keep modinfo and version sections. */
23292401
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
2402+
info->sechdrs[info->index.vers_ext_crc].sh_flags &=
2403+
~(unsigned long)SHF_ALLOC;
2404+
info->sechdrs[info->index.vers_ext_name].sh_flags &=
2405+
~(unsigned long)SHF_ALLOC;
23302406
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
23312407

23322408
return 0;

kernel/module/version.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,28 @@ int check_version(const struct load_info *info,
1919
unsigned int versindex = info->index.vers;
2020
unsigned int i, num_versions;
2121
struct modversion_info *versions;
22+
struct modversion_info_ext version_ext;
2223

2324
/* Exporting module didn't supply crcs? OK, we're already tainted. */
2425
if (!crc)
2526
return 1;
2627

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+
}
43+
2744
/* No versions at all? modprobe --force does this. */
2845
if (versindex == 0)
2946
return try_to_force_load(mod, symname) == 0;
@@ -87,6 +104,34 @@ int same_magic(const char *amagic, const char *bmagic,
87104
return strcmp(amagic, bmagic) == 0;
88105
}
89106

107+
void modversion_ext_start(const struct load_info *info,
108+
struct modversion_info_ext *start)
109+
{
110+
unsigned int crc_idx = info->index.vers_ext_crc;
111+
unsigned int name_idx = info->index.vers_ext_name;
112+
Elf_Shdr *sechdrs = info->sechdrs;
113+
114+
/*
115+
* Both of these fields are needed for this to be useful
116+
* Any future fields should be initialized to NULL if absent.
117+
*/
118+
if (crc_idx == 0 || name_idx == 0) {
119+
start->remaining = 0;
120+
return;
121+
}
122+
123+
start->crc = (const u32 *)sechdrs[crc_idx].sh_addr;
124+
start->name = (const char *)sechdrs[name_idx].sh_addr;
125+
start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
126+
}
127+
128+
void modversion_ext_advance(struct modversion_info_ext *vers)
129+
{
130+
vers->remaining--;
131+
vers->crc++;
132+
vers->name += strlen(vers->name) + 1;
133+
}
134+
90135
/*
91136
* Generate the signature for all relevant module structures here.
92137
* If these change, we don't want to try to parse the module.

0 commit comments

Comments
 (0)