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: Matthew Maurer <mmau...@google.com>
---
 kernel/module/main.c | 106 ++++++++++++++++++++++++++++---------------
 1 file changed, 69 insertions(+), 37 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index c480fd33861a..252cfa9eee67 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -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;
                }
        }
-- 
2.46.0.rc2.264.g509ed76dc8-goog


Reply via email to