[PATCH] module: change to print useful messages from elf_validity_check()

From: Shuah Khan
Date: Mon Oct 04 2021 - 16:06:32 EST


elf_validity_check() checks ELF headers for errors and ELF Spec.
compliance and if any of them fail it returns -ENOEXEC from all of
these error paths. Almost all of them don't print any messages.

When elf_validity_check() returns an error, load_module() prints an
error message without error code. It is hard to determine why the
module ELF structure is invalid, even if load_module() prints the
error code which is -ENOEXEC in all of these cases.

Change to print useful error messages from elf_validity_check() to
clearly say what went wrong and why the ELF validity checks failed.

Remove the load_module() error message which is no longer needed.

Signed-off-by: Shuah Khan <skhan@xxxxxxxxxxxxxxxxxxx>
---
kernel/module.c | 83 ++++++++++++++++++++++++++++++++++++-------------
1 file changed, 61 insertions(+), 22 deletions(-)

diff --git a/kernel/module.c b/kernel/module.c
index 40ec9a030eec..d972786b10ba 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2942,15 +2942,18 @@ static int module_sig_check(struct load_info *info, int flags)

static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr)
{
- unsigned long secend;
+ unsigned long secend; /* too short when sh_offset is Elf64_Off */

/*
* Check for both overflow and offset/size being
* too large.
*/
secend = shdr->sh_offset + shdr->sh_size;
- if (secend < shdr->sh_offset || secend > info->len)
+ if (secend < shdr->sh_offset || secend > info->len) {
+ pr_err("Invalid ELF section offset/size: secend(%lu) < shdr->sh_offset(%llu) or secend(%lu) > e_shnum(%lu)\n",
+ secend, shdr->sh_offset, secend, info->len);
return -ENOEXEC;
+ }

return 0;
}
@@ -2967,14 +2970,31 @@ static int elf_validity_check(struct load_info *info)
Elf_Shdr *shdr, *strhdr;
int err;

- if (info->len < sizeof(*(info->hdr)))
- return -ENOEXEC;
+ if (info->len < sizeof(*(info->hdr))) {
+ pr_err("Invalid ELF header len %lu < %lu\n", info->len,
+ sizeof(*(info->hdr)));
+ goto no_exec;
+ }

- if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
- || info->hdr->e_type != ET_REL
- || !elf_check_arch(info->hdr)
- || info->hdr->e_shentsize != sizeof(Elf_Shdr))
- return -ENOEXEC;
+ if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ pr_err("Invalid ELF header magic: != %s\n", ELFMAG);
+ goto no_exec;
+ }
+ if (info->hdr->e_type != ET_REL) {
+ pr_err("Invalid ELF header type: %u != %u\n",
+ info->hdr->e_type, ET_REL);
+ goto no_exec;
+ }
+ if (!elf_check_arch(info->hdr)) {
+ pr_err("Invalid architecture in ELF header: %u\n",
+ info->hdr->e_machine);
+ goto no_exec;
+ }
+ if (info->hdr->e_shentsize != sizeof(Elf_Shdr)) {
+ pr_err("Invalid ELF section header size %d != %lu\n",
+ info->hdr->e_shentsize, sizeof(Elf_Shdr));
+ goto no_exec;
+ }

/*
* e_shnum is 16 bits, and sizeof(Elf_Shdr) is
@@ -2983,8 +3003,12 @@ static int elf_validity_check(struct load_info *info)
*/
if (info->hdr->e_shoff >= info->len
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
- info->len - info->hdr->e_shoff))
- return -ENOEXEC;
+ info->len - info->hdr->e_shoff)) {
+ pr_err("Invalid ELF section header overflow: %ld > %llu\n",
+ info->hdr->e_shnum * sizeof(Elf_Shdr),
+ info->len - info->hdr->e_shoff);
+ goto no_exec;
+ }

info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;

@@ -2992,8 +3016,12 @@ static int elf_validity_check(struct load_info *info)
* Verify if the section name table index is valid.
*/
if (info->hdr->e_shstrndx == SHN_UNDEF
- || info->hdr->e_shstrndx >= info->hdr->e_shnum)
- return -ENOEXEC;
+ || 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];
err = validate_section_offset(info, strhdr);
@@ -3006,8 +3034,10 @@ static int elf_validity_check(struct load_info *info)
* strings in the section safe.
*/
info->secstrings = (void *)info->hdr + strhdr->sh_offset;
- if (info->secstrings[strhdr->sh_size - 1] != '\0')
- return -ENOEXEC;
+ if (info->secstrings[strhdr->sh_size - 1] != '\0') {
+ pr_err("ELF Spec violation: section name table isn't null terminated\n");
+ goto no_exec;
+ }

/*
* The code assumes that section 0 has a length of zero and
@@ -3015,8 +3045,12 @@ static int elf_validity_check(struct load_info *info)
*/
if (info->sechdrs[0].sh_type != SHT_NULL
|| info->sechdrs[0].sh_size != 0
- || info->sechdrs[0].sh_addr != 0)
- return -ENOEXEC;
+ || info->sechdrs[0].sh_addr != 0) {
+ pr_err("ELF Spec violation: section 0 type!=SH_NULL(%d) or non-zero len(%llu) or addr(%llu)\n",
+ info->sechdrs[0].sh_type, info->sechdrs[0].sh_size,
+ info->sechdrs[0].sh_addr);
+ goto no_exec;
+ }

for (i = 1; i < info->hdr->e_shnum; i++) {
shdr = &info->sechdrs[i];
@@ -3026,8 +3060,12 @@ static int elf_validity_check(struct load_info *info)
continue;
case SHT_SYMTAB:
if (shdr->sh_link == SHN_UNDEF
- || shdr->sh_link >= info->hdr->e_shnum)
- return -ENOEXEC;
+ || shdr->sh_link >= info->hdr->e_shnum) {
+ pr_err("Invalid ELF sh_link!=SHN_UNDEF(%d) or (sh_link(%d) >= hdr->e_shnum(%d)\n",
+ shdr->sh_link, shdr->sh_link,
+ info->hdr->e_shnum);
+ goto no_exec;
+ }
fallthrough;
default:
err = validate_section_offset(info, shdr);
@@ -3049,6 +3087,9 @@ static int elf_validity_check(struct load_info *info)
}

return 0;
+
+no_exec:
+ return -ENOEXEC;
}

#define COPY_CHUNK_SIZE (16*PAGE_SIZE)
@@ -3940,10 +3981,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
* sections.
*/
err = elf_validity_check(info);
- if (err) {
- pr_err("Module has invalid ELF structures\n");
+ if (err)
goto free_copy;
- }

/*
* Everything checks out, so set up the section info
--
2.30.2