libelf: treat phdr and shdr similarly
authorJan Beulich <jbeulich@suse.com>
Wed, 7 Dec 2016 12:53:50 +0000 (13:53 +0100)
committerJan Beulich <jbeulich@suse.com>
Wed, 7 Dec 2016 12:53:50 +0000 (13:53 +0100)
Just like elf_shdr_count(), elf_phdr_count() better bounds checks the
value.

Add table entry size checks to elf_init().

Also both program and section headers are optional, and hence their
checking better is done conditionally only when any such headers are
present.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
xen/common/libelf/libelf-loader.c
xen/common/libelf/libelf-tools.c

index a72cd8a274fe7d64b546c4b0af0a464a4b2faef5..544cfc5b556c60008e9f244545d36e1d301a0feb 100644 (file)
@@ -52,24 +52,45 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
     elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]);
     elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]);
 
-    /* Sanity check phdr. */
-    offset = elf_uval(elf, elf->ehdr, e_phoff) +
-        elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf);
-    if ( offset > elf->size )
+    /* Sanity check phdr if present. */
+    count = elf_phdr_count(elf);
+    if ( count )
     {
-        elf_err(elf, "ELF: phdr overflow (off %" PRIx64 " > size %lx)\n",
-                offset, (unsigned long)elf->size);
-        return -1;
+        if ( elf_uval(elf, elf->ehdr, e_phentsize) <
+             elf_size(elf, ELF_HANDLE_DECL(elf_phdr)) )
+        {
+            elf_err(elf, "ELF: phdr too small (%" PRIu64 ")\n",
+                    elf_uval(elf, elf->ehdr, e_phentsize));
+            return -1;
+        }
+        offset = elf_uval(elf, elf->ehdr, e_phoff) +
+            elf_uval(elf, elf->ehdr, e_phentsize) * count;
+        if ( offset > elf->size )
+        {
+            elf_err(elf, "ELF: phdr overflow (off %" PRIx64 " > size %lx)\n",
+                    offset, (unsigned long)elf->size);
+            return -1;
+        }
     }
 
-    /* Sanity check shdr. */
-    offset = elf_uval(elf, elf->ehdr, e_shoff) +
-        elf_uval(elf, elf->ehdr, e_shentsize) * elf_shdr_count(elf);
-    if ( offset > elf->size )
+    /* Sanity check shdr if present. */
+    count = elf_shdr_count(elf);
+    if ( count )
     {
-        elf_err(elf, "ELF: shdr overflow (off %" PRIx64 " > size %lx)\n",
-                offset, (unsigned long)elf->size);
-        return -1;
+        if ( elf_uval(elf, elf->ehdr, e_shentsize) < elf_size(elf, shdr) )
+        {
+            elf_err(elf, "ELF: shdr too small (%" PRIu64 ")\n",
+                    elf_uval(elf, elf->ehdr, e_shentsize));
+            return -1;
+        }
+        offset = elf_uval(elf, elf->ehdr, e_shoff) +
+            elf_uval(elf, elf->ehdr, e_shentsize) * count;
+        if ( offset > elf->size )
+        {
+            elf_err(elf, "ELF: shdr overflow (off %" PRIx64 " > size %lx)\n",
+                    offset, (unsigned long)elf->size);
+            return -1;
+        }
     }
 
     /* Find section string table. */
@@ -79,7 +100,6 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
         elf->sec_strtab = elf_section_start(elf, shdr);
 
     /* Find symbol table and symbol string table. */
-    count = elf_shdr_count(elf);
     for ( i = 1; i < count; i++ )
     {
         shdr = elf_shdr_by_index(elf, i);
index a9edb6a8dcb348b1de77966dd96e6c2dcc82d4ef..bf661e7090f0d8de707e64c78ee72e312f3f9145 100644 (file)
@@ -130,8 +130,11 @@ uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr)
 unsigned elf_shdr_count(struct elf_binary *elf)
 {
     unsigned count = elf_uval(elf, elf->ehdr, e_shnum);
-    uint64_t max = elf->size / sizeof(Elf32_Shdr);
+    uint64_t max;
 
+    if ( !count )
+        return 0;
+    max = elf->size / elf_uval(elf, elf->ehdr, e_shentsize);
     if ( max > UINT_MAX )
         max = UINT_MAX;
     if ( count > max )
@@ -144,7 +147,20 @@ unsigned elf_shdr_count(struct elf_binary *elf)
 
 unsigned elf_phdr_count(struct elf_binary *elf)
 {
-    return elf_uval(elf, elf->ehdr, e_phnum);
+    unsigned count = elf_uval(elf, elf->ehdr, e_phnum);
+    uint64_t max;
+
+    if ( !count )
+        return 0;
+    max = elf->size / elf_uval(elf, elf->ehdr, e_phentsize);
+    if ( max > UINT_MAX )
+        max = UINT_MAX;
+    if ( count > max )
+    {
+        elf_mark_broken(elf, "far too many program headers");
+        count = max;
+    }
+    return count;
 }
 
 ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_name(struct elf_binary *elf, const char *name)