FROMGIT: 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. Change-Id: Ia951a6ec89d114184913262c839b42ffcfd6a5e9 Signed-off-by: Matthew Maurer <mmaurer@google.com> Reviewed-by: Sami Tolvanen <samitolvanen@google.com> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org> (cherry picked from commit 3c5700aeabd87e81d9153a7666b28d0e405c6c88 kernel/git/modules/linux.git modules-next) Bug: 347787665
This commit is contained in:
committed by
Matthias Männich
parent
b86e9d4333
commit
3112326a5f
+69
-37
@@ -1825,6 +1825,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.
|
||||
@@ -1848,7 +1913,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;
|
||||
@@ -1857,34 +1922,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];
|
||||
@@ -1913,14 +1953,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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user