mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
module: Factor out elf_validity_cache_sechdrs
Factor out and document the validation of section headers. Because we now validate all section offsets and lengths before accessing them, we can remove the ad-hoc checks. 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
90f8f312db
commit
c92aab819d
@ -1708,6 +1708,87 @@ static int elf_validity_ehdr(const struct load_info *info)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elf_validity_cache_sechdrs() - Cache section headers if valid
|
||||||
|
* @info: Load info to compute section headers from
|
||||||
|
*
|
||||||
|
* Checks:
|
||||||
|
*
|
||||||
|
* * ELF header is valid (see elf_validity_ehdr())
|
||||||
|
* * Section headers are the size we expect
|
||||||
|
* * Section array fits in the user provided data
|
||||||
|
* * Section index 0 is NULL
|
||||||
|
* * Section contents are inbounds
|
||||||
|
*
|
||||||
|
* Then updates @info with a &load_info->sechdrs pointer if valid.
|
||||||
|
*
|
||||||
|
* Return: %0 if valid, negative error code if validation failed.
|
||||||
|
*/
|
||||||
|
static int elf_validity_cache_sechdrs(struct load_info *info)
|
||||||
|
{
|
||||||
|
Elf_Shdr *sechdrs;
|
||||||
|
Elf_Shdr *shdr;
|
||||||
|
int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = elf_validity_ehdr(info);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (info->hdr->e_shentsize != sizeof(Elf_Shdr)) {
|
||||||
|
pr_err("Invalid ELF section header size\n");
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* e_shnum is 16 bits, and sizeof(Elf_Shdr) is
|
||||||
|
* known and small. So e_shnum * sizeof(Elf_Shdr)
|
||||||
|
* will not overflow unsigned long on any platform.
|
||||||
|
*/
|
||||||
|
if (info->hdr->e_shoff >= info->len
|
||||||
|
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
|
||||||
|
info->len - info->hdr->e_shoff)) {
|
||||||
|
pr_err("Invalid ELF section header overflow\n");
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
sechdrs = (void *)info->hdr + info->hdr->e_shoff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The code assumes that section 0 has a length of zero and
|
||||||
|
* an addr of zero, so check for it.
|
||||||
|
*/
|
||||||
|
if (sechdrs[0].sh_type != SHT_NULL
|
||||||
|
|| sechdrs[0].sh_size != 0
|
||||||
|
|| sechdrs[0].sh_addr != 0) {
|
||||||
|
pr_err("ELF Spec violation: section 0 type(%d)!=SH_NULL or non-zero len or addr\n",
|
||||||
|
sechdrs[0].sh_type);
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate contents are inbounds */
|
||||||
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
||||||
|
shdr = &sechdrs[i];
|
||||||
|
switch (shdr->sh_type) {
|
||||||
|
case SHT_NULL:
|
||||||
|
case SHT_NOBITS:
|
||||||
|
/* No contents, offset/size don't mean anything */
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
err = validate_section_offset(info, shdr);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("Invalid ELF section in module (section %u type %u)\n",
|
||||||
|
i, shdr->sh_type);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info->sechdrs = sechdrs;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check userspace passed ELF module against our expectations, and cache
|
* Check userspace passed ELF module against our expectations, and cache
|
||||||
* useful variables for further processing as we go.
|
* useful variables for further processing as we go.
|
||||||
@ -1737,29 +1818,10 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
|
|||||||
unsigned int num_info_secs = 0, info_idx;
|
unsigned int num_info_secs = 0, info_idx;
|
||||||
unsigned int num_sym_secs = 0, sym_idx;
|
unsigned int num_sym_secs = 0, sym_idx;
|
||||||
|
|
||||||
err = elf_validity_ehdr(info);
|
err = elf_validity_cache_sechdrs(info);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (info->hdr->e_shentsize != sizeof(Elf_Shdr)) {
|
|
||||||
pr_err("Invalid ELF section header size\n");
|
|
||||||
goto no_exec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* e_shnum is 16 bits, and sizeof(Elf_Shdr) is
|
|
||||||
* known and small. So e_shnum * sizeof(Elf_Shdr)
|
|
||||||
* will not overflow unsigned long on any platform.
|
|
||||||
*/
|
|
||||||
if (info->hdr->e_shoff >= info->len
|
|
||||||
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
|
|
||||||
info->len - info->hdr->e_shoff)) {
|
|
||||||
pr_err("Invalid ELF section header overflow\n");
|
|
||||||
goto no_exec;
|
|
||||||
}
|
|
||||||
|
|
||||||
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify if the section name table index is valid.
|
* Verify if the section name table index is valid.
|
||||||
*/
|
*/
|
||||||
@ -1772,11 +1834,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
|
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
|
||||||
err = validate_section_offset(info, strhdr);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Invalid ELF section hdr(type %u)\n", strhdr->sh_type);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The section name table must be NUL-terminated, as required
|
* The section name table must be NUL-terminated, as required
|
||||||
@ -1793,18 +1850,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
|
|||||||
goto no_exec;
|
goto no_exec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The code assumes that section 0 has a length of zero and
|
|
||||||
* an addr of zero, so check for it.
|
|
||||||
*/
|
|
||||||
if (info->sechdrs[0].sh_type != SHT_NULL
|
|
||||||
|| info->sechdrs[0].sh_size != 0
|
|
||||||
|| info->sechdrs[0].sh_addr != 0) {
|
|
||||||
pr_err("ELF Spec violation: section 0 type(%d)!=SH_NULL or non-zero len or addr\n",
|
|
||||||
info->sechdrs[0].sh_type);
|
|
||||||
goto no_exec;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i < info->hdr->e_shnum; i++) {
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
||||||
shdr = &info->sechdrs[i];
|
shdr = &info->sechdrs[i];
|
||||||
switch (shdr->sh_type) {
|
switch (shdr->sh_type) {
|
||||||
@ -1823,12 +1868,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
|
|||||||
sym_idx = i;
|
sym_idx = i;
|
||||||
fallthrough;
|
fallthrough;
|
||||||
default:
|
default:
|
||||||
err = validate_section_offset(info, shdr);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Invalid ELF section in module (section %u type %u)\n",
|
|
||||||
i, shdr->sh_type);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if (strcmp(info->secstrings + shdr->sh_name,
|
if (strcmp(info->secstrings + shdr->sh_name,
|
||||||
".gnu.linkonce.this_module") == 0) {
|
".gnu.linkonce.this_module") == 0) {
|
||||||
num_mod_secs++;
|
num_mod_secs++;
|
||||||
|
Loading…
Reference in New Issue
Block a user