summaryrefslogtreecommitdiff
path: root/src/elfops/check.c
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-05-26 22:16:54 +0100
committernorly <ny-git@enpas.org>2013-05-27 03:10:31 +0100
commitf5eac562c915811c27969949c178f8fe96a12e47 (patch)
tree7713d3c13719825ed96210f2acc4c34d796597b8 /src/elfops/check.c
parent3d899fbfd33a8b44dcbd7dfbc952464731a7e589 (diff)
Validate input at libelf level
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.
Diffstat (limited to 'src/elfops/check.c')
-rw-r--r--src/elfops/check.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/elfops/check.c b/src/elfops/check.c
new file mode 100644
index 0000000..c7ebc7d
--- /dev/null
+++ b/src/elfops/check.c
@@ -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;
+}