diff options
author | Matthew Maurer <mmaurer@google.com> | 2024-10-16 01:16:38 +0200 |
---|---|---|
committer | Luis Chamberlain <mcgrof@kernel.org> | 2024-10-19 23:35:07 +0200 |
commit | 3c5700aeabd87e81d9153a7666b28d0e405c6c88 (patch) | |
tree | b4f7ec59dbccd58bc0ac9d238f186b72e69b7253 /kernel/module | |
parent | module: Factor out elf_validity_cache_sechdrs (diff) | |
download | linux-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.c | 106 |
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; } } |