https://sourceware.org/bugzilla/show_bug.cgi?id=23528

--- Comment #1 from Mark Wielaard <mark at klomp dot org> ---
Replicated under valgrind:

$ valgrind -q eu-readelf -S Double-free-libelf

==13892== Invalid free() / delete / delete[] / realloc()
==13892==    at 0x48369EB: free (vg_replace_malloc.c:530)
==13892==    by 0x4899094: elf_end (elf_end.c:171)
==13892==    by 0x4868619: free_file (dwfl_module.c:57)
==13892==    by 0x4868787: __libdwfl_module_free (dwfl_module.c:113)
==13892==    by 0x4868370: dwfl_end (dwfl_end.c:54)
==13892==    by 0x114617: process_file (readelf.c:873)
==13892==    by 0x111C13: main (readelf.c:350)
==13892==  Address 0x51138b0 is 0 bytes inside a block of size 36 free'd
==13892==    at 0x48369EB: free (vg_replace_malloc.c:530)
==13892==    by 0x48A5C46: __libelf_reset_rawdata (elf_compress.c:325)
==13892==    by 0x48A5D80: elf_compress (elf_compress.c:514)
==13892==    by 0x4869F00: relocate_section (relocate.c:507)
==13892==    by 0x486A567: __libdwfl_relocate (relocate.c:752)
==13892==    by 0x486EB5B: dwfl_module_getelf (dwfl_module_getelf.c:52)
==13892==    by 0x11E764: process_elf_file (readelf.c:901)
==13892==    by 0x11E764: process_dwflmod (readelf.c:760)
==13892==    by 0x486C450: dwfl_getmodules (dwfl_getmodules.c:86)
==13892==    by 0x1143BF: process_file (readelf.c:868)
==13892==    by 0x111C13: main (readelf.c:350)
==13892==  Block was alloc'd at
==13892==    at 0x48357BF: malloc (vg_replace_malloc.c:299)
==13892==    by 0x48A59E7: __libelf_decompress (elf_compress.c:223)
==13892==    by 0x48A60D4: elf_compress_gnu (elf_compress_gnu.c:178)
==13892==    by 0x4869F68: relocate_section (relocate.c:504)
==13892==    by 0x486A567: __libdwfl_relocate (relocate.c:752)
==13892==    by 0x486EB5B: dwfl_module_getelf (dwfl_module_getelf.c:52)
==13892==    by 0x11E764: process_elf_file (readelf.c:901)
==13892==    by 0x11E764: process_dwflmod (readelf.c:760)
==13892==    by 0x486C450: dwfl_getmodules (dwfl_getmodules.c:86)
==13892==    by 0x1143BF: process_file (readelf.c:868)
==13892==    by 0x111C13: main (readelf.c:350)

The issue is this section:

[10] .zdebug_abbrev       PROGBITS     00000000 00011d 00002b  0 NGTC   0   0 
1

Note how it claims to be both gnu style compressed (starts with .zdebug) and
gabi compressed (has SHF_COMPRESSED flag C).

Then the code in relocate.c tries to decompress the section data twice:

  if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
    elf_compress_gnu (tscn, 0, 0);

  if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
    if (elf_compress (tscn, 0, 0) < 0)
      return DWFL_E_LIBELF;

So it tries to decompress twice. The second call sees that the data is already
decompressed and assumes it was done implicitly by some call to elf_strptr, and
so just uses the decompressed data to setup the decompressed section data
again, throwing away the buffer that elf_compress_gnu has already setup.
elf_end then tries to free this data buffer again.

The most direct fix is to just make relocate.c not decompress twice in
different ways:

diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
index 9afcdebe..81750cd3 100644
--- a/libdwfl/relocate.c
+++ b/libdwfl/relocate.c
@@ -238,8 +238,7 @@ resolve_symbol (Dwfl_Module *referer, struct
reloc_symtab_cache *symtab,
          /* If the section is already decompressed, that isn't an error.  */
          if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
            elf_compress_gnu (scn, 0, 0);
-
-         if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+         else if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
            if (elf_compress (scn, 0, 0) < 0)
              return DWFL_E_LIBELF;

@@ -502,8 +501,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const
GElf_Ehdr *ehdr,

   if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
     elf_compress_gnu (tscn, 0, 0);
-
-  if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
+  else if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
     if (elf_compress (tscn, 0, 0) < 0)
       return DWFL_E_LIBELF;

@@ -523,8 +521,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const
GElf_Ehdr *ehdr,

   if (strncmp (sname, ".zdebug", strlen ("zdebug")) == 0)
     elf_compress_gnu (scn, 0, 0);
-
-  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+  else if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
     if (elf_compress (scn, 0, 0) < 0)
       return DWFL_E_LIBELF;


But we can probably fix it in elf_compress[_gnu] by better checks or just by
refusing to elf_compress_gnu if the section already has the SHF_COMPRESSED flag
set.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to