libelf: elf_getphdrnum sanity check the returned phnum result.
authorMark Wielaard <mjw@redhat.com>
Sat, 22 Nov 2014 16:33:05 +0000 (17:33 +0100)
committerMark Wielaard <mjw@redhat.com>
Wed, 26 Nov 2014 19:12:52 +0000 (20:12 +0100)
The internal __elf_getphdrnum_rdlock might return an inconsistent phnum.
Return a sanitized value, or return an error to users that rely on phnum
to be consistent. That way iterating over all phdrs using elf_getphdr
will return consistent results.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
libelf/ChangeLog
libelf/elf_getphdrnum.c

index c7e8d30..b21714e 100644 (file)
@@ -1,3 +1,8 @@
+2014-11-22  Mark Wielaard  <mjw@redhat.com>
+
+       * elf_getphdrnum.c (elf_getphdrnum): Sanity check the
+       __elf_getphdrnum_rdlock result.
+
 2014-11-18  Mark Wielaard  <mjw@redhat.com>
 
        * version_xlate.h (elf_cvt_Verdef): Check for overflow.
index d8e34d7..63c27fb 100644 (file)
@@ -97,6 +97,39 @@ elf_getphdrnum (elf, dst)
 
   rwlock_rdlock (elf->lock);
   result = __elf_getphdrnum_rdlock (elf, dst);
+
+  /* Do some sanity checking to make sure phnum and phoff are consistent.  */
+  Elf64_Off off = (elf->class == ELFCLASS32
+                  ? elf->state.elf32.ehdr->e_phoff
+                  : elf->state.elf64.ehdr->e_phoff);
+  if (unlikely (off == 0))
+    {
+      *dst = 0;
+      goto out;
+    }
+
+  if (unlikely (off >= elf->maximum_size))
+    {
+      __libelf_seterrno (ELF_E_INVALID_DATA);
+      result = -1;
+      goto out;
+    }
+
+  /* Check for too many sections.  */
+  size_t phdr_size = (elf->class == ELFCLASS32
+                     ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr));
+  if (unlikely (*dst > SIZE_MAX / phdr_size))
+    {
+      __libelf_seterrno (ELF_E_INVALID_DATA);
+      result = -1;
+      goto out;
+    }
+
+  /* Truncated file?  Don't return more than can be indexed.  */
+  if (unlikely (elf->maximum_size - off < *dst * phdr_size))
+    *dst = (elf->maximum_size - off) / phdr_size;
+
+out:
   rwlock_unlock (elf->lock);
 
   return result;