Bug 25077 - unstrip bad handling of sh_entsize of the symver section
Summary: unstrip bad handling of sh_entsize of the symver section
Status: RESOLVED FIXED
Alias: None
Product: elfutils
Classification: Unclassified
Component: tools (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Mark Wielaard
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-10-08 03:19 UTC by leftcopy.chx
Modified: 2019-10-26 00:28 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2019-10-19 00:00:00


Attachments
pocs and error message (53.51 KB, application/x-bzip)
2019-10-08 03:19 UTC, leftcopy.chx
Details

Note You need to log in before you can comment on or make changes to this bug.
Description leftcopy.chx 2019-10-08 03:19:11 UTC
Created attachment 12030 [details]
pocs and error message

As of 47780c9e, libelf may suffer from a heap-buffer-overflow vulnerability when executing `./eu-unstrip $FILE ./stripped -o /dev/null`, where elfutils is built with ASAN. The attachment contains the required files for reproduction. The error message is like:


==32515==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6150000009d6 at pc 0x7ffff6e670ae bp 0x7fffffff1290 sp 0x7fffffff0a38
READ of size 60432 at 0x6150000009d6 thread T0
    #0 0x7ffff6e670ad  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x4d0ad)
    #1 0x7ffff6c10de4 in pwrite_retry ../lib/system.h:95
    #2 0x7ffff6c10de4 in __elf64_updatefile /home/hongxu/FOT/Targets/elfutils/eu-orig/libelf/elf32_updatefile.c:772
    #3 0x7ffff6c0ce98 in write_file /home/hongxu/FOT/Targets/elfutils/eu-orig/libelf/elf_update.c:132
    #4 0x7ffff6c0ce98 in elf_update /home/hongxu/FOT/Targets/elfutils/eu-orig/libelf/elf_update.c:231
    #5 0x55555556b4ec in copy_elided_sections /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2074
    #6 0x55555556bea1 in handle_file /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2162
    #7 0x55555556c760 in handle_explicit_files /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2227
    #8 0x55555556f1f6 in main /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2562
    #9 0x7ffff6596b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #10 0x555555559a89 in _start (/home/hongxu/FOT/Targets/elfutils/eu-asan/install/bin/eu-unstrip+0x5a89)

0x6150000009d6 is located 0 bytes to the right of 470-byte region [0x615000000800,0x6150000009d6)
allocated by thread T0 here:
    #0 0x7ffff6ef8d38 in __interceptor_calloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xded38)
    #1 0x55555556f4aa in xcalloc /home/hongxu/FOT/Targets/elfutils/eu-asan/lib/xmalloc.c:63
    #2 0x55555555db16 in adjust_relocs /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:565
    #3 0x55555556a62a in copy_elided_sections /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:1960
    #4 0x55555556bea1 in handle_file /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2162
    #5 0x55555556c760 in handle_explicit_files /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2227
    #6 0x55555556f1f6 in main /home/hongxu/FOT/Targets/elfutils/eu-asan/src/unstrip.c:2562
    #7 0x7ffff6596b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x4d0ad) 
Shadow bytes around the buggy address:
  0x0c2a7fff80e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fa
  0x0c2a7fff80f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff8110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff8120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2a7fff8130: 00 00 00 00 00 00 00 00 00 00[06]fa fa fa fa fa
  0x0c2a7fff8140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==32515==ABORTING
Comment 1 Mark Wielaard 2019-10-19 12:43:32 UTC
Replicated under valgrind:

$ valgrind -q eu-unstrip hbo_libelf/hbo__elf32_updatefile.c:772_1 hbo_libelf/stripped -o /tmp/foobar
==25850== Syscall param pwrite64(buf) points to unaddressable byte(s)
==25850==    at 0x57A80D3: __pwrite_nocancel (syscall-template.S:81)
==25850==    by 0x4E45E37: UnknownInlinedFun (system.h:95)
==25850==    by 0x4E45E37: __elf64_updatefile (elf32_updatefile.c:795)
==25850==    by 0x4E42250: write_file (elf_update.c:132)
==25850==    by 0x4E42250: elf_update (elf_update.c:231)
==25850==    by 0x406840: copy_elided_sections (unstrip.c:2070)
==25850==    by 0x4078B3: handle_file (unstrip.c:2158)
==25850==    by 0x407B8B: handle_explicit_files (unstrip.c:2223)
==25850==    by 0x4029DD: main (unstrip.c:2558)
==25850==  Address 0x632b8c6 is 0 bytes after a block of size 470 alloc'd
==25850==    at 0x4C2BF79: calloc (vg_replace_malloc.c:762)
==25850==    by 0x408028: xcalloc (xmalloc.c:63)
==25850==    by 0x403FD6: adjust_relocs.isra.14 (unstrip.c:565)
==25850==    by 0x406CC6: copy_elided_sections (unstrip.c:1956)
==25850==    by 0x4078B3: handle_file (unstrip.c:2158)
==25850==    by 0x407B8B: handle_explicit_files (unstrip.c:2223)
==25850==    by 0x4029DD: main (unstrip.c:2558)
==25850== 
eu-unstrip: cannot write output file: cannot write data to file

The issue is simply that if the sh_entsize of the symver section was bogus (bigger than necessary) then some bogus data would be written out (except that then fails as can be seen by the error message).

The solution is simply to use the actual symver data size:

diff --git a/src/unstrip.c b/src/unstrip.c
index fc878325..5531a02d 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -572,7 +572,7 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr,
 
        record_new_data (versym);
        data->d_buf = versym;
-       data->d_size = nent * shdr->sh_entsize;
+       data->d_size = nent * sizeof versym[0];
        elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
        update_sh_size (outscn, data);
       }
Comment 2 leftcopy.chx 2019-10-22 06:17:21 UTC
Since this crashes occurs when calling `elf_update`, which resides in libelf, I suppose this is a libelf library issue.

I'd suggest adding some documents to warn that it is the developers' duty to ensure that the claimed symver size matches with the actual one, or better resolves the size purely inside libelf code?
Comment 3 Mark Wielaard 2019-10-22 12:00:50 UTC
(In reply to leftcopy.chx from comment #2)
> Since this crashes occurs when calling `elf_update`, which resides in
> libelf, I suppose this is a libelf library issue.
> 
> I'd suggest adding some documents to warn that it is the developers' duty to
> ensure that the claimed symver size matches with the actual one, or better
> resolves the size purely inside libelf code?

libelf would do the right thing if you use don't use elf_flagelf (outelf, ELF_C_SET, ELF_F_LAYOUT) as eu-unstrip does. If you set ELF_F_LAYOUT you promise to set up all ELF data fields correctly yourself and libelf just trusts you when writing out the file.
Comment 4 leftcopy.chx 2019-10-22 12:12:27 UTC
OK, that makes sense!
Comment 5 Mark Wielaard 2019-10-26 00:28:33 UTC
commit da5a32a400da6a03a96f0aff10aff2d86bd9baad (HEAD -> master)
Author: Mark Wielaard <mark@klomp.org>
Date:   Sat Oct 19 14:37:46 2019 +0200

    unstrip: Don't try to write extra bogus versym data.
    
    If the sh_entsize of the symver section was bogus (bigger than necessary)
    then some bogus data would be written out (except that then fails because
    pwrite would probably fail). Fix that by ignoring the bogus sh_entsize
    and use the actual symver data size.
    
    https://sourceware.org/bugzilla/show_bug.cgi?id=25077
    
    Signed-off-by: Mark Wielaard <mark@klomp.org>