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:
Matthew Maurer 2024-10-15 23:16:37 +00:00 committed by Luis Chamberlain
parent 90f8f312db
commit c92aab819d

View File

@ -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++;