Validate input at libelf level
authornorly <ny-git@enpas.org>
Sun, 26 May 2013 21:16:54 +0000 (22:16 +0100)
committernorly <ny-git@enpas.org>
Mon, 27 May 2013 02:10:31 +0000 (03:10 +0100)
This way we can just assume that stuff works later on and keep the code
clean and simple. It especially establishes that the file has a sane
format and is thus understandable and editable.

include/libelfu/elfops.h
include/libelfu/generic.h
src/elfops/check.c [new file with mode: 0644]
src/model/fromFile.c

index 58da4cf2f17970c8408ec88e38a35b41f3ae39b9..cf40090d7f2cfa0afc129cde71ca269d36ab94f6 100644 (file)
@@ -7,6 +7,8 @@
 #include <libelfu/types.h>
 
 
+int elfu_eCheck(Elf *e);
+
    char* elfu_eScnName(Elf *e, Elf_Scn *scn);
 Elf_Scn* elfu_eScnByName(Elf *e, char *name);
 
index f5b0e0f030e7ccba0c8e3746131b15a042f33217..09d2ffc651f0d372815027c5d72896fae5068d6b 100644 (file)
@@ -3,9 +3,22 @@
 
 #include <libelf/gelf.h>
 
+#define OFFS_END(off, sz) ((off) + (sz))
+
+#define OVERLAPPING(off1, sz1, off2, sz2) \
+  (!((off1) == (off2) && ((sz1 == 0) || (sz2 == 0))) \
+   && (((off1) <= (off2) && (off2) < OFFS_END((off1), (sz1))) \
+       || ((off2) <= (off1) && (off1) < OFFS_END((off2), (sz2)))) \
+  )
+
+#define FULLY_OVERLAPPING(off1, sz1, off2, sz2) \
+  (((off1) <= (off2) && OFFS_END((off2), (sz2)) <= OFFS_END((off1), (sz1))) \
+   || ((off2) <= (off1) && OFFS_END((off1), (sz1)) <= OFFS_END((off2), (sz2))))
+
 
-size_t elfu_gScnSizeFile(const GElf_Shdr *shdr);
 
 int elfu_gPhdrContainsScn(GElf_Phdr *phdr, GElf_Shdr *shdr);
 
+size_t elfu_gScnSizeFile(const GElf_Shdr *shdr);
+
 #endif
diff --git a/src/elfops/check.c b/src/elfops/check.c
new file mode 100644 (file)
index 0000000..c7ebc7d
--- /dev/null
@@ -0,0 +1,180 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <libelf/libelf.h>
+#include <libelf/gelf.h>
+#include <libelfu/libelfu.h>
+
+int elfu_eCheck(Elf *e)
+{
+  int elfclass;
+  GElf_Ehdr ehdr;
+  GElf_Phdr *phdrs = NULL;
+  GElf_Shdr *shdrs = NULL;
+  size_t i, j, numPhdr, numShdr;
+  int retval = 0;
+
+  assert(e);
+
+  elfclass = gelf_getclass(e);
+  if (elfclass == ELFCLASSNONE) {
+    ELFU_WARNELF("getclass");
+    goto ERROR;
+  }
+
+  if (!gelf_getehdr(e, &ehdr)) {
+    ELFU_WARNELF("gelf_getehdr");
+    goto ERROR;
+  }
+
+  if (elf_getphdrnum(e, &numPhdr)) {
+    ELFU_WARNELF("elf_getphdrnum");
+    goto ERROR;
+  }
+
+  if (elf_getshdrnum(e, &numShdr)) {
+    ELFU_WARNELF("elf_getshdrnum");
+    goto ERROR;
+  }
+
+
+  if (numPhdr > 0) {
+    phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
+    if (!phdrs) {
+      ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
+      goto ERROR;
+    }
+
+    /* Attempt to load all PHDRs at once to catch any errors early */
+    for (i = 0; i < numPhdr; i++) {
+      GElf_Phdr phdr;
+      if (gelf_getphdr(e, i, &phdr) != &phdr) {
+        ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
+        goto ERROR;
+      }
+
+      phdrs[i] = phdr;
+    }
+
+    /* Check that LOAD PHDR memory ranges do not overlap, and that others
+     * are either fully contained in a LOAD range, or not at all. */
+    for (i = 0; i < numPhdr; i++) {
+      if (phdrs[i].p_type != PT_LOAD) {
+        continue;
+      }
+
+      for (j = 0; j < numPhdr; j++) {
+        if (j == i || phdrs[j].p_type != PT_LOAD) {
+          continue;
+        }
+
+        if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
+                        phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
+          if (phdrs[j].p_type == PT_LOAD) {
+            ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
+            goto ERROR;
+          } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
+                                        phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
+            ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
+            goto ERROR;
+          }
+        }
+      }
+    }
+  }
+
+
+  if (numShdr > 1) {
+    /* SHDRs should not overlap with PHDRs. */
+    if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
+                    ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
+      ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
+      goto ERROR;
+    }
+
+    shdrs = malloc(numShdr * sizeof(GElf_Shdr));
+    if (!shdrs) {
+      ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
+      goto ERROR;
+    }
+
+    /* Attempt to load all SHDRs at once to catch any errors early */
+    for (i = 1; i < numShdr; i++) {
+      Elf_Scn *scn;
+      GElf_Shdr shdr;
+
+      scn = elf_getscn(e, i);
+      if (!scn) {
+        ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
+      }
+
+      if (gelf_getshdr(scn, &shdr) != &shdr) {
+        ELFU_WARNELF("gelf_getshdr");
+        goto ERROR;
+      }
+
+      shdrs[i] = shdr;
+    }
+
+
+    /* Check that Section memory ranges do not overlap.
+     * NB: Section 0 is reserved and thus ignored. */
+    for (i = 1; i < numShdr; i++) {
+      /* Section should not overlap with EHDR. */
+      if (shdrs[i].sh_offset == 0) {
+        ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
+        goto ERROR;
+      }
+
+      /* Section should not overlap with PHDRs. */
+      if (OVERLAPPING(shdrs[i].sh_offset, elfu_gScnSizeFile(&shdrs[i]),
+                      ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
+        ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
+        goto ERROR;
+      }
+
+      /* Section should not overlap with SHDRs. */
+      if (OVERLAPPING(shdrs[i].sh_offset, elfu_gScnSizeFile(&shdrs[i]),
+                      ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
+        ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
+        goto ERROR;
+      }
+
+      for (j = 1; j < numShdr; j++) {
+        if (j == i) {
+          continue;
+        }
+
+        /* Sections must not overlap in memory. */
+        if (shdrs[i].sh_addr != 0
+            && shdrs[j].sh_addr != 0
+            && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
+                           shdrs[j].sh_addr, shdrs[j].sh_size)) {
+          ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
+          goto ERROR;
+        }
+
+        /* Sections must not overlap in file. */
+        if (OVERLAPPING(shdrs[i].sh_offset, elfu_gScnSizeFile(&shdrs[i]),
+                        shdrs[j].sh_offset, elfu_gScnSizeFile(&shdrs[j]))) {
+          ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
+          goto ERROR;
+        }
+      }
+    }
+  }
+
+
+  DONE:
+  if (phdrs) {
+    free(phdrs);
+  }
+  if (shdrs) {
+    free(shdrs);
+  }
+  return retval;
+
+  ERROR:
+  ELFU_WARN("elfu_eCheck: Errors found.\n");
+  retval = -1;
+  goto DONE;
+}
index fee6ec03fabd4cc08ddef6572757c42d821c7bed..57cb51971bb84016e08ad07202b23a0657330db9 100644 (file)
@@ -94,6 +94,12 @@ ElfuElf* elfu_mFromElf(Elf *e)
   size_t shstrndx;
   size_t i, n;
 
+  assert(e);
+  if (elfu_eCheck(e)) {
+    goto ERROR;
+  }
+
+  /* Get the section string table index */
   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
     shstrndx = 0;
   }
@@ -117,7 +123,7 @@ ElfuElf* elfu_mFromElf(Elf *e)
 
   if (!gelf_getehdr(e, &me->ehdr)) {
     ELFU_WARNELF("gelf_getehdr");
-    goto out;
+    goto ERROR;
   }
 
 
@@ -135,7 +141,7 @@ ElfuElf* elfu_mFromElf(Elf *e)
         me->shstrtab = ms;
       }
     } else {
-      goto out;
+      goto ERROR;
     }
 
     scn = elf_nextscn(e, scn);
@@ -165,15 +171,16 @@ ElfuElf* elfu_mFromElf(Elf *e)
     if (mp) {
       CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
     } else {
-      goto out;
+      goto ERROR;
     }
   }
 
+  return me;
 
 
-  return me;
+  ERROR:
+  // TODO: Free data structures
 
-  out:
-  // FIXME
+  ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
   return NULL;
 }