Commit c92aab81 authored by Matthew Maurer's avatar Matthew Maurer Committed by Luis Chamberlain
Browse files

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: default avatarMatthew Maurer <mmaurer@google.com>
Reviewed-by: default avatarSami Tolvanen <samitolvanen@google.com>
Signed-off-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
parent 90f8f312
Loading
Loading
Loading
Loading
+82 −43
Original line number Diff line number Diff line
@@ -1708,6 +1708,87 @@ static int elf_validity_ehdr(const struct load_info *info)
	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
 * 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_sym_secs = 0, sym_idx;

	err = elf_validity_ehdr(info);
	err = elf_validity_cache_sechdrs(info);
	if (err < 0)
		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.
	 */
@@ -1772,11 +1834,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
	}

	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
@@ -1793,18 +1850,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
		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++) {
		shdr = &info->sechdrs[i];
		switch (shdr->sh_type) {
@@ -1823,12 +1868,6 @@ static int elf_validity_cache_copy(struct load_info *info, int flags)
			sym_idx = i;
			fallthrough;
		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,
				   ".gnu.linkonce.this_module") == 0) {
				num_mod_secs++;