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

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: default avatarMatthew Maurer <mmaurer@google.com>
Reviewed-by: default avatarSami Tolvanen <samitolvanen@google.com>
Signed-off-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
parent c92aab81
Loading
Loading
Loading
Loading
+69 −37
Original line number Diff line number Diff line
@@ -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;
		}
	}