mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 02:05:33 +00:00
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>
This commit is contained in:
parent
c92aab819d
commit
3c5700aeab
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user