Re: [PATCH v3] module: harden ELF info handling

2021-01-18 Thread Jessica Yu

+++ Frank van der Linden [14/01/21 22:21 +]:

5fdc7db644 ("module: setup load info before module_sig_check()")
moved the ELF setup, so that it was done before the signature
check. This made the module name available to signature error
messages.

However, the checks for ELF correctness in setup_load_info
are not sufficient to prevent bad memory references due to
corrupted offset fields, indices, etc.

So, there's a regression in behavior here: a corrupt and unsigned
(or badly signed) module, which might previously have been rejected
immediately, can now cause an oops/crash.

Harden ELF handling for module loading by doing the following:

- Move the signature check back up so that it comes before ELF
 initialization. It's best to do the signature check to see
 if we can trust the module, before using the ELF structures
 inside it. This also makes checks against info->len
 more accurate again, as this field will be reduced by the
 length of the signature in mod_check_sig().

 The module name is now once again not available for error
 messages during the signature check, but that seems like
 a fair tradeoff.

- Check if sections have offset / size fields that at least don't
 exceed the length of the module.

- Check if sections have section name offsets that don't fall
 outside the section name table.

- Add a few other sanity checks against invalid section indices,
 etc.

This is not an exhaustive consistency check, but the idea is to
at least get through the signature and blacklist checks without
crashing because of corrupted ELF info, and to error out gracefully
for most issues that would have caused problems later on.

Fixes: 5fdc7db644 ("module: setup load info before module_sig_check()")
Signed-off-by: Frank van der Linden 


Queued on modules-next.

Thanks Frank!

Jessica



[PATCH v3] module: harden ELF info handling

2021-01-14 Thread Frank van der Linden
5fdc7db644 ("module: setup load info before module_sig_check()")
moved the ELF setup, so that it was done before the signature
check. This made the module name available to signature error
messages.

However, the checks for ELF correctness in setup_load_info
are not sufficient to prevent bad memory references due to
corrupted offset fields, indices, etc.

So, there's a regression in behavior here: a corrupt and unsigned
(or badly signed) module, which might previously have been rejected
immediately, can now cause an oops/crash.

Harden ELF handling for module loading by doing the following:

- Move the signature check back up so that it comes before ELF
  initialization. It's best to do the signature check to see
  if we can trust the module, before using the ELF structures
  inside it. This also makes checks against info->len
  more accurate again, as this field will be reduced by the
  length of the signature in mod_check_sig().

  The module name is now once again not available for error
  messages during the signature check, but that seems like
  a fair tradeoff.

- Check if sections have offset / size fields that at least don't
  exceed the length of the module.

- Check if sections have section name offsets that don't fall
  outside the section name table.

- Add a few other sanity checks against invalid section indices,
  etc.

This is not an exhaustive consistency check, but the idea is to
at least get through the signature and blacklist checks without
crashing because of corrupted ELF info, and to error out gracefully
for most issues that would have caused problems later on.

Fixes: 5fdc7db644 ("module: setup load info before module_sig_check()")
Signed-off-by: Frank van der Linden 
---
 kernel/module.c   | 143 --
 kernel/module_signature.c |   2 +-
 kernel/module_signing.c   |   2 +-
 3 files changed, 126 insertions(+), 21 deletions(-)

diff --git a/kernel/module.c b/kernel/module.c
index 4bf30e4b3eaa..fda42c0064db 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2964,7 +2964,7 @@ static int module_sig_check(struct load_info *info, int 
flags)
}
 
if (is_module_sig_enforced()) {
-   pr_notice("%s: loading of %s is rejected\n", info->name, 
reason);
+   pr_notice("Loading of %s is rejected\n", reason);
return -EKEYREJECTED;
}
 
@@ -2977,9 +2977,33 @@ static int module_sig_check(struct load_info *info, int 
flags)
 }
 #endif /* !CONFIG_MODULE_SIG */
 
-/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
-static int elf_header_check(struct load_info *info)
+static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr)
 {
+   unsigned long secend;
+
+   /*
+* 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)
+   return -ENOEXEC;
+
+   return 0;
+}
+
+/*
+ * Sanity checks against invalid binaries, wrong arch, weird elf version.
+ *
+ * Also do basic validity checks against section offsets and sizes, the
+ * section name string table, and the indices used for it (sh_name).
+ */
+static int elf_validity_check(struct load_info *info)
+{
+   unsigned int i;
+   Elf_Shdr *shdr, *strhdr;
+   int err;
+
if (info->len < sizeof(*(info->hdr)))
return -ENOEXEC;
 
@@ -2989,11 +3013,78 @@ static int elf_header_check(struct load_info *info)
|| info->hdr->e_shentsize != sizeof(Elf_Shdr))
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))
return -ENOEXEC;
 
+   info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
+
+   /*
+* 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;
+
+   strhdr = >sechdrs[info->hdr->e_shstrndx];
+   err = validate_section_offset(info, strhdr);
+   if (err < 0)
+   return err;
+
+   /*
+* 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 (info->secstrings[strhdr->sh_size - 1] != '\0')
+   return -ENOEXEC;
+
+   /*
+* The code assumes that section 0 has a length of zero and
+* an addr of zero, so check for it.
+*/
+   if