Subject: libelf: fix stack memory leak when loading 32 bit symbol tables
From: Roger Pau Monné roger.pau@citrix.com Tue Nov 22 14:13:55 2016 +0100
Date: Tue Nov 22 14:13:55 2016 +0100:
Git: 8ce2238f197fde118e291932d869a8a6c10ac539

The 32 bit Elf structs are smaller than the 64 bit ones, which means that
when loading them there's some padding left uninitialized at the end of each
struct (because the size indicated in e_ehsize and e_shentsize is
smaller than the size of elf_ehdr and elf_shdr).

Fix this by introducing a new helper that is used to set
[caller_]xdest_{base/size} and that takes care of performing the appropriate
memset of the region. This newly introduced helper is then used to set and
unset xdest_{base/size} in elf_load_bsdsyms. Now that the full struct
is zeroed, there's no need to specifically zero the undefined section.

This is CVE-2016-9384 / XSA-194.

Suggested-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>

Also remove the open coded (and redundant with the earlier
elf_memset_unchecked()) use of caller_xdest_* from elf_init().

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
master commit: fb08f7d009a64b96efa4462c9d19ed6881936859
master date: 2016-11-22 13:48:30 +0100

diff --git a/xen/common/libelf/libelf-loader.c b/xen/common/libelf/libelf-loader.c
index 4d3ae4d..bc1f87b 100644
--- a/xen/common/libelf/libelf-loader.c
+++ b/xen/common/libelf/libelf-loader.c
@@ -43,8 +43,6 @@ elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t
     elf->ehdr = ELF_MAKE_HANDLE(elf_ehdr, (elf_ptrval)image_input);
     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]);
-    elf->caller_xdest_base = NULL;
-    elf->caller_xdest_size = 0;
 
     /* Sanity check phdr. */
     offset = elf_uval(elf, elf->ehdr, e_phoff) +
@@ -284,9 +282,8 @@ do {                                                                \
 #define SYMTAB_INDEX    1
 #define STRTAB_INDEX    2
 
-    /* Allow elf_memcpy_safe to write to symbol_header. */
-    elf->caller_xdest_base = &header;
-    elf->caller_xdest_size = sizeof(header);
+    /* Allow elf_memcpy_safe to write to header. */
+    elf_set_xdest(elf, &header, sizeof(header));
 
     /*
      * Calculate the position of the various elements in GUEST MEMORY SPACE.
@@ -319,11 +316,7 @@ do {                                                                \
     elf_store_field_bitness(elf, header_handle, e_phentsize, 0);
     elf_store_field_bitness(elf, header_handle, e_phnum, 0);
 
-    /* Zero the undefined section. */
-    section_handle = ELF_MAKE_HANDLE(elf_shdr,
-                     ELF_REALPTR2PTRVAL(&header.elf_header.section[SHN_UNDEF]));
     shdr_size = elf_uval(elf, elf->ehdr, e_shentsize);
-    elf_memset_safe(elf, ELF_HANDLE_PTRVAL(section_handle), 0, shdr_size);
 
     /*
      * The symtab section header is going to reside in section[SYMTAB_INDEX],
@@ -404,8 +397,7 @@ do {                                                                \
     }
 
     /* Remove permissions from elf_memcpy_safe. */
-    elf->caller_xdest_base = NULL;
-    elf->caller_xdest_size = 0;
+    elf_set_xdest(elf, NULL, 0);
 
 #undef SYMTAB_INDEX
 #undef STRTAB_INDEX
diff --git a/xen/common/libelf/libelf-tools.c b/xen/common/libelf/libelf-tools.c
index 5a4757b..e73e729 100644
--- a/xen/common/libelf/libelf-tools.c
+++ b/xen/common/libelf/libelf-tools.c
@@ -59,8 +59,7 @@ bool elf_access_ok(struct elf_binary * elf,
         return 1;
     if ( elf_ptrval_in_range(ptrval, size, elf->dest_base, elf->dest_size) )
         return 1;
-    if ( elf_ptrval_in_range(ptrval, size,
-                             elf->caller_xdest_base, elf->caller_xdest_size) )
+    if ( elf_ptrval_in_range(ptrval, size, elf->xdest_base, elf->xdest_size) )
         return 1;
     elf_mark_broken(elf, "out of range access");
     return 0;
@@ -373,6 +372,14 @@ bool elf_phdr_is_loadable(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr
     return ((p_type == PT_LOAD) && (p_flags & (PF_R | PF_W | PF_X)) != 0);
 }
 
+void elf_set_xdest(struct elf_binary *elf, void *addr, uint64_t size)
+{
+    elf->xdest_base = addr;
+    elf->xdest_size = size;
+    if ( addr != NULL )
+        elf_memset_safe(elf, ELF_REALPTR2PTRVAL(addr), 0, size);
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/xen/libelf.h b/xen/include/xen/libelf.h
index 95b5370..cf62bc7 100644
--- a/xen/include/xen/libelf.h
+++ b/xen/include/xen/libelf.h
@@ -210,13 +210,11 @@ struct elf_binary {
     uint64_t bsd_symtab_pend;
 
     /*
-     * caller's other acceptable destination
-     *
-     * Again, these are trusted and must be valid (or 0) so long
-     * as the struct elf_binary is in use.
+     * caller's other acceptable destination.
+     * Set by elf_set_xdest.  Do not set these directly.
      */
-    void *caller_xdest_base;
-    uint64_t caller_xdest_size;
+    void *xdest_base;
+    uint64_t xdest_size;
 
 #ifndef __XEN__
     /* misc */
@@ -494,5 +492,10 @@ static inline void ELF_ADVANCE_DEST(struct elf_binary *elf, uint64_t amount)
     }
 }
 
+/* Specify a (single) additional destination, to which the image may
+ * cause writes.  As with dest_base and dest_size, the values provided
+ * are trusted and must be valid so long as the struct elf_binary
+ * is in use or until elf_set_xdest(,0,0) is called. */
+void elf_set_xdest(struct elf_binary *elf, void *addr, uint64_t size);
 
 #endif /* __XEN_LIBELF_H__ */
