* [PATCH v3 14/16] modules: Support extended MODVERSIONS info
[not found] <20240806212106.617164-1-mmaurer@google.com>
@ 2024-08-06 21:20 ` Matthew Maurer
2024-08-15 20:33 ` Sami Tolvanen
2024-08-16 23:04 ` Michael Ellerman
0 siblings, 2 replies; 5+ messages in thread
From: Matthew Maurer @ 2024-08-06 21:20 UTC (permalink / raw)
To: masahiroy, ndesaulniers, ojeda, gary, mcgrof, Michael Ellerman,
Alex Gaynor, Wedson Almeida Filho, Christophe Leroy,
Matthew Maurer, Naveen N Rao
Cc: Andreas Hindborg, rust-for-linux, linux-kbuild, linuxppc-dev,
Boqun Feng, marcan, linux-kernel, Nicholas Piggin,
Björn Roy Baron, Alice Ryhl, asahi, Benno Lossin, neal, j,
linux-modules
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.
Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
arch/powerpc/kernel/module_64.c | 24 ++++++++-
kernel/module/internal.h | 11 ++++
kernel/module/main.c | 92 ++++++++++++++++++++++++++++++---
kernel/module/version.c | 43 +++++++++++++++
4 files changed, 160 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index 7112adc597a8..15b74c9a1df1 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -355,6 +355,24 @@ static void dedotify_versions(struct modversion_info *vers,
}
}
+static void dedotify_ext_version_names(char *str_seq, unsigned long size)
+{
+ unsigned long out = 0;
+ unsigned long in;
+ char last = '\0';
+
+ for (in = 0; in < size; in++) {
+ if (last == '\0')
+ /* Skip all leading dots */
+ if (str_seq[in] == '.')
+ continue;
+ last = str_seq[in];
+ str_seq[out++] = last;
+ }
+ /* Zero the trailing portion of the names table for robustness */
+ memset(&str_seq[out], 0, size - out);
+}
+
/*
* Undefined symbols which refer to .funcname, hack to funcname. Make .TOC.
* seem to be defined (value set later).
@@ -424,10 +442,12 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
me->arch.toc_section = i;
if (sechdrs[i].sh_addralign < 8)
sechdrs[i].sh_addralign = 8;
- }
- else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
+ } else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") == 0)
dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size);
+ else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") == 0)
+ dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset,
+ sechdrs[i].sh_size);
if (sechdrs[i].sh_type == SHT_SYMTAB)
dedotify((void *)hdr + sechdrs[i].sh_offset,
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index daef2be83902..59959c21b205 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -86,6 +86,8 @@ struct load_info {
unsigned int vers;
unsigned int info;
unsigned int pcpu;
+ unsigned int vers_ext_crc;
+ unsigned int vers_ext_name;
} index;
};
@@ -389,6 +391,15 @@ void module_layout(struct module *mod, struct modversion_info *ver, struct kerne
struct kernel_symbol *ks, struct tracepoint * const *tp);
int check_modstruct_version(const struct load_info *info, struct module *mod);
int same_magic(const char *amagic, const char *bmagic, bool has_crcs);
+struct modversion_info_ext {
+ size_t remaining;
+ const s32 *crc;
+ const char *name;
+};
+void modversion_ext_start(const struct load_info *info, struct modversion_info_ext *ver);
+void modversion_ext_advance(struct modversion_info_ext *ver);
+#define for_each_modversion_info_ext(ver, info) \
+ for (modversion_ext_start(info, &ver); ver.remaining > 0; modversion_ext_advance(&ver))
#else /* !CONFIG_MODVERSIONS */
static inline int check_version(const struct load_info *info,
const char *symname,
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 7001054c5c4f..ba63ea1b6ad5 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2039,6 +2039,82 @@ static int elf_validity_cache_index_str(struct load_info *info)
return 0;
}
+/**
+ * elf_validity_cache_index_versions() - Validate and cache version indices
+ * @info: Load info to cache version indices in.
+ * Must have &load_info->sechdrs and &load_info->secstrings populated.
+ * @flags: Load flags, relevant to suppress version loading, see
+ * uapi/linux/module.h
+ *
+ * If we're ignoring modversions based on @flags, zero all version indices
+ * and return validity. Othewrise check:
+ *
+ * * If "__version_ext_crcs" is present, "__version_ext_names" is present
+ * * There is a name present for every crc
+ *
+ * Then populate:
+ *
+ * * &load_info->index.vers
+ * * &load_info->index.vers_ext_crc
+ * * &load_info->index.vers_ext_names
+ *
+ * if present.
+ *
+ * Return: %0 if valid, %-ENOEXEC on failure.
+ */
+static int elf_validity_cache_index_versions(struct load_info *info, int flags)
+{
+ unsigned int vers_ext_crc;
+ unsigned int vers_ext_name;
+ size_t crc_count;
+ size_t remaining_len;
+ size_t name_size;
+ char *name;
+
+ /* If modversions were suppressed, pretend we didn't find any */
+ if (flags & MODULE_INIT_IGNORE_MODVERSIONS) {
+ info->index.vers = 0;
+ info->index.vers_ext_crc = 0;
+ info->index.vers_ext_name = 0;
+ return 0;
+ }
+
+ vers_ext_crc = find_sec(info, "__version_ext_crcs");
+ vers_ext_name = find_sec(info, "__version_ext_names");
+
+ /* If we have one field, we must have the other */
+ if (!!vers_ext_crc != !!vers_ext_name) {
+ pr_err("extended version crc+name presence does not match");
+ return -ENOEXEC;
+ }
+
+ /*
+ * If we have extended version information, we should have the same
+ * number of entries in every section.
+ */
+ if (vers_ext_crc) {
+ crc_count = info->sechdrs[vers_ext_crc].sh_size / sizeof(s32);
+ name = (void *)info->hdr +
+ info->sechdrs[vers_ext_name].sh_offset;
+ remaining_len = info->sechdrs[vers_ext_name].sh_size;
+
+ while (crc_count--) {
+ name_size = strnlen(name, remaining_len) + 1;
+ if (name_size > remaining_len) {
+ pr_err("more extended version crcs than names");
+ return -ENOEXEC;
+ }
+ remaining_len -= name_size;
+ name += name_size;
+ }
+ }
+
+ info->index.vers = find_sec(info, "__versions");
+ info->index.vers_ext_crc = vers_ext_crc;
+ info->index.vers_ext_name = vers_ext_name;
+ return 0;
+}
+
/**
* elf_validity_cache_index() - Resolve, validate, cache section indices
* @info: Load info to read from and update.
@@ -2053,9 +2129,7 @@ static int elf_validity_cache_index_str(struct load_info *info)
* * elf_validity_cache_index_mod()
* * elf_validity_cache_index_sym()
* * elf_validity_cache_index_str()
- *
- * If versioning is not suppressed via flags, load the version index from
- * a section called "__versions" with no validation.
+ * * elf_validity_cache_index_versions()
*
* If CONFIG_SMP is enabled, load the percpu section by name with no
* validation.
@@ -2078,11 +2152,9 @@ static int elf_validity_cache_index(struct load_info *info, int flags)
err = elf_validity_cache_index_str(info);
if (err < 0)
return err;
-
- if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
- info->index.vers = 0; /* Pretend no __versions section! */
- else
- info->index.vers = find_sec(info, "__versions");
+ err = elf_validity_cache_index_versions(info, flags);
+ if (err < 0)
+ return err;
info->index.pcpu = find_pcpusec(info);
@@ -2293,6 +2365,10 @@ static int rewrite_section_headers(struct load_info *info, int flags)
/* Track but don't keep modinfo and version sections. */
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ info->sechdrs[info->index.vers_ext_crc].sh_flags &=
+ ~(unsigned long)SHF_ALLOC;
+ info->sechdrs[info->index.vers_ext_name].sh_flags &=
+ ~(unsigned long)SHF_ALLOC;
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
return 0;
diff --git a/kernel/module/version.c b/kernel/module/version.c
index 53f43ac5a73e..02d8340bdb57 100644
--- a/kernel/module/version.c
+++ b/kernel/module/version.c
@@ -19,11 +19,28 @@ int check_version(const struct load_info *info,
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;
@@ -87,6 +104,32 @@ int same_magic(const char *amagic, const char *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;
+
+ start->crc = (const s32 *)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.
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 14/16] modules: Support extended MODVERSIONS info
2024-08-06 21:20 ` [PATCH v3 14/16] modules: Support extended MODVERSIONS info Matthew Maurer
@ 2024-08-15 20:33 ` Sami Tolvanen
2024-08-16 23:04 ` Michael Ellerman
1 sibling, 0 replies; 5+ messages in thread
From: Sami Tolvanen @ 2024-08-15 20:33 UTC (permalink / raw)
To: Matthew Maurer
Cc: masahiroy, ndesaulniers, ojeda, gary, mcgrof, Michael Ellerman,
Alex Gaynor, Wedson Almeida Filho, Christophe Leroy, Naveen N Rao,
rust-for-linux, linux-kbuild, linux-kernel, neal, marcan, j,
asahi, Nicholas Piggin, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, linuxppc-dev,
linux-modules
Hi Matt,
On Tue, Aug 6, 2024 at 9:25 PM Matthew Maurer <mmaurer@google.com> wrote:
>
[...]
> +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))
nit: The extra parentheses are not necessary.
> + start->remaining = 0;
> +
> + start->crc = (const s32 *)sechdrs[crc_idx].sh_addr;
> + start->name = (const char *)sechdrs[name_idx].sh_addr;
> + start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
> +}
Is this missing an else condition or a return? Why set
start->remaining to zero and then proceed to assign a possibly invalid
value to it anyway?
Sami
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 14/16] modules: Support extended MODVERSIONS info
2024-08-06 21:20 ` [PATCH v3 14/16] modules: Support extended MODVERSIONS info Matthew Maurer
2024-08-15 20:33 ` Sami Tolvanen
@ 2024-08-16 23:04 ` Michael Ellerman
2024-08-19 23:41 ` Matthew Maurer
1 sibling, 1 reply; 5+ messages in thread
From: Michael Ellerman @ 2024-08-16 23:04 UTC (permalink / raw)
To: Matthew Maurer, masahiroy, ndesaulniers, ojeda, gary, mcgrof,
Alex Gaynor, Wedson Almeida Filho, Christophe Leroy,
Matthew Maurer, Naveen N Rao
Cc: rust-for-linux, linux-kbuild, linux-kernel, neal, marcan, j,
asahi, Nicholas Piggin, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, linuxppc-dev,
linux-modules
Matthew Maurer <mmaurer@google.com> writes:
> 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.
AFAICS the existing code only strips a single leading dot, not all
leading dots?
cheers
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 14/16] modules: Support extended MODVERSIONS info
2024-08-16 23:04 ` Michael Ellerman
@ 2024-08-19 23:41 ` Matthew Maurer
2024-08-20 2:46 ` Michael Ellerman
0 siblings, 1 reply; 5+ messages in thread
From: Matthew Maurer @ 2024-08-19 23:41 UTC (permalink / raw)
To: Michael Ellerman
Cc: masahiroy, ndesaulniers, ojeda, gary, mcgrof, Alex Gaynor,
Wedson Almeida Filho, Christophe Leroy, Naveen N Rao,
rust-for-linux, linux-kbuild, linux-kernel, neal, marcan, j,
asahi, Nicholas Piggin, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, linuxppc-dev,
linux-modules
On Fri, Aug 16, 2024 at 4:04 PM Michael Ellerman <mpe@ellerman.id.au> wrote:
>
> Matthew Maurer <mmaurer@google.com> writes:
> > 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.
>
> AFAICS the existing code only strips a single leading dot, not all
> leading dots?
You appear to be correct, I'll update that in the next version, but
want to wait for more feedback on the rest of the patchset before
sending up another full series.
>
> cheers
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 14/16] modules: Support extended MODVERSIONS info
2024-08-19 23:41 ` Matthew Maurer
@ 2024-08-20 2:46 ` Michael Ellerman
0 siblings, 0 replies; 5+ messages in thread
From: Michael Ellerman @ 2024-08-20 2:46 UTC (permalink / raw)
To: Matthew Maurer
Cc: masahiroy, ndesaulniers, ojeda, gary, mcgrof, Alex Gaynor,
Wedson Almeida Filho, Christophe Leroy, Naveen N Rao,
rust-for-linux, linux-kbuild, linux-kernel, neal, marcan, j,
asahi, Nicholas Piggin, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, linuxppc-dev,
linux-modules
Matthew Maurer <mmaurer@google.com> writes:
> On Fri, Aug 16, 2024 at 4:04 PM Michael Ellerman <mpe@ellerman.id.au> wrote:
>> Matthew Maurer <mmaurer@google.com> writes:
>> > 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.
>>
>> AFAICS the existing code only strips a single leading dot, not all
>> leading dots?
>
> You appear to be correct, I'll update that in the next version, but
> want to wait for more feedback on the rest of the patchset before
> sending up another full series.
Yep, no worries.
cheers
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-08-20 2:46 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20240806212106.617164-1-mmaurer@google.com>
2024-08-06 21:20 ` [PATCH v3 14/16] modules: Support extended MODVERSIONS info Matthew Maurer
2024-08-15 20:33 ` Sami Tolvanen
2024-08-16 23:04 ` Michael Ellerman
2024-08-19 23:41 ` Matthew Maurer
2024-08-20 2:46 ` Michael Ellerman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).