summaryrefslogtreecommitdiffstats
path: root/kernel/module
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2024-10-16 01:16:38 +0200
committerLuis Chamberlain <mcgrof@kernel.org>2024-10-19 23:35:07 +0200
commit3c5700aeabd87e81d9153a7666b28d0e405c6c88 (patch)
treeb4f7ec59dbccd58bc0ac9d238f186b72e69b7253 /kernel/module
parentmodule: Factor out elf_validity_cache_sechdrs (diff)
downloadlinux-3c5700aeabd87e81d9153a7666b28d0e405c6c88.tar.xz
linux-3c5700aeabd87e81d9153a7666b28d0e405c6c88.zip
module: Factor out elf_validity_cache_secstrings
Factor out the validation of section names. There are two behavioral changes: 1. Previously, we did not validate non-SHF_ALLOC sections. This may have once been safe, as find_sec skips non-SHF_ALLOC sections, but find_any_sec, which will be used to load BTF if that is enabled, ignores the SHF_ALLOC flag. Since there's no need to support invalid section names, validate all of them, not just SHF_ALLOC sections. 2. Section names were validated *after* accessing them for the purposes of detecting ".modinfo" and ".gnu.linkonce.this_module". They are now checked prior to the access, which could avoid bad accesses with malformed modules. Signed-off-by: Matthew Maurer <mmaurer@google.com> Reviewed-by: Sami Tolvanen <samitolvanen@google.com> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
Diffstat (limited to 'kernel/module')
-rw-r--r--kernel/module/main.c106
1 files changed, 69 insertions, 37 deletions
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 1f3a07ee59c6..6a9159afca02 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1789,6 +1789,71 @@ static int elf_validity_cache_sechdrs(struct load_info *info)
return 0;
}
+/**
+ * elf_validity_cache_secstrings() - Caches section names if valid
+ * @info: Load info to cache section names from. Must have valid sechdrs.
+ *
+ * Specifically checks:
+ *
+ * * Section name table index is inbounds of section headers
+ * * Section name table is not empty
+ * * Section name table is NUL terminated
+ * * All section name offsets are inbounds of the section
+ *
+ * Then updates @info with a &load_info->secstrings pointer if valid.
+ *
+ * Return: %0 if valid, negative error code if validation failed.
+ */
+static int elf_validity_cache_secstrings(struct load_info *info)
+{
+ Elf_Shdr *strhdr, *shdr;
+ char *secstrings;
+ int i;
+
+ /*
+ * Verify if the section name table index is valid.
+ */
+ if (info->hdr->e_shstrndx == SHN_UNDEF
+ || info->hdr->e_shstrndx >= info->hdr->e_shnum) {
+ pr_err("Invalid ELF section name index: %d || e_shstrndx (%d) >= e_shnum (%d)\n",
+ info->hdr->e_shstrndx, info->hdr->e_shstrndx,
+ info->hdr->e_shnum);
+ return -ENOEXEC;
+ }
+
+ strhdr = &info->sechdrs[info->hdr->e_shstrndx];
+
+ /*
+ * The section name table must be NUL-terminated, as required
+ * by the spec. This makes strcmp and pr_* calls that access
+ * strings in the section safe.
+ */
+ secstrings = (void *)info->hdr + strhdr->sh_offset;
+ if (strhdr->sh_size == 0) {
+ pr_err("empty section name table\n");
+ return -ENOEXEC;
+ }
+ if (secstrings[strhdr->sh_size - 1] != '\0') {
+ pr_err("ELF Spec violation: section name table isn't null terminated\n");
+ return -ENOEXEC;
+ }
+
+ for (i = 0; i < info->hdr->e_shnum; i++) {
+ shdr = &info->sechdrs[i];
+ /* SHT_NULL means sh_name has an undefined value */
+ if (shdr->sh_type == SHT_NULL)
+ continue;
+ if (shdr->sh_name >= strhdr->sh_size) {
+ pr_err("Invalid ELF section name in module (section %u type %u)\n",
+ i, shdr->sh_type);
+ return -ENOEXEC;
+ }
+ }
+
+ info->secstrings = secstrings;
+ return 0;
+}
+
/*
* Check userspace passed ELF module against our expectations, and cache
* useful variables for further processing as we go.
@@ -1812,7 +1877,7 @@ static int elf_validity_cache_sechdrs(struct load_info *info)
static int elf_validity_cache_copy(struct load_info *info, int flags)
{
unsigned int i;
- Elf_Shdr *shdr, *strhdr;
+ Elf_Shdr *shdr;
int err;
unsigned int num_mod_secs = 0, mod_idx;
unsigned int num_info_secs = 0, info_idx;
@@ -1821,34 +1886,9 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
err = elf_validity_cache_sechdrs(info);
if (err < 0)
return err;
-
- /*
- * Verify if the section name table index is valid.
- */
- if (info->hdr->e_shstrndx == SHN_UNDEF
- || info->hdr->e_shstrndx >= info->hdr->e_shnum) {
- pr_err("Invalid ELF section name index: %d || e_shstrndx (%d) >= e_shnum (%d)\n",
- info->hdr->e_shstrndx, info->hdr->e_shstrndx,
- info->hdr->e_shnum);
- goto no_exec;
- }
-
- strhdr = &info->sechdrs[info->hdr->e_shstrndx];
-
- /*
- * The section name table must be NUL-terminated, as required
- * by the spec. This makes strcmp and pr_* calls that access
- * strings in the section safe.
- */
- info->secstrings = (void *)info->hdr + strhdr->sh_offset;
- if (strhdr->sh_size == 0) {
- pr_err("empty section name table\n");
- goto no_exec;
- }
- if (info->secstrings[strhdr->sh_size - 1] != '\0') {
- pr_err("ELF Spec violation: section name table isn't null terminated\n");
- goto no_exec;
- }
+ err = elf_validity_cache_secstrings(info);
+ if (err < 0)
+ return err;
for (i = 1; i < info->hdr->e_shnum; i++) {
shdr = &info->sechdrs[i];
@@ -1877,14 +1917,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
num_info_secs++;
info_idx = i;
}
-
- if (shdr->sh_flags & SHF_ALLOC) {
- if (shdr->sh_name >= strhdr->sh_size) {
- pr_err("Invalid ELF section name in module (section %u type %u)\n",
- i, shdr->sh_type);
- return -ENOEXEC;
- }
- }
break;
}
}