Bug 24116 - A Heap-buffer-overflow problem was discovered in the function print_debug_line_section in readelf.c
Summary: A Heap-buffer-overflow problem was discovered in the function print_debug_lin...
Status: RESOLVED FIXED
Alias: None
Product: elfutils
Classification: Unclassified
Component: tools (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-01-22 08:58 UTC by wcventure
Modified: 2019-02-01 08:12 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
POC1 (1.42 KB, application/x-object)
2019-01-22 08:58 UTC, wcventure
Details
POC2 (1.48 KB, application/x-object)
2019-01-22 08:59 UTC, wcventure
Details
POC3 (1.47 KB, application/x-object)
2019-01-22 08:59 UTC, wcventure
Details
Regression (9.46 KB, application/octet-stream)
2019-02-01 07:31 UTC, wcventure
Details

Note You need to log in before you can comment on or make changes to this bug.
Description wcventure 2019-01-22 08:58:46 UTC
Created attachment 11559 [details]
POC1

Hi, 

A Heap-buffer-overflow problem was discovered in the function print_debug_line_section in readelf.c, as distributed in ELFutils 0.175. A crafted ELF input can cause segment faults and I have confirmed them with address sanitizer too.

Here are the POC files. Please use "./eu-readelf -w $POC" to reproduce the error.

$git log

> commit de01cc6f9446187d69b9748bb3636361c79e77a4
> Author: Mark Wielaard <mark@klomp.org>
> Date:   Wed Jan 16 15:41:31 2019 +0100
> 
>     libebl: Check NT_PLATFORM core notes contain a zero terminated string.
> 
>     Most strings in core notes are fixed size. But NT_PLATFORM contains just
>     a variable length string. Check that it is actually zero terminated
>     before passing to readelf to print.
> 
>     https://sourceware.org/bugzilla/show_bug.cgi?id=24089
> 
>     Signed-off-by: Mark Wielaard <mark@klomp.org>

The ASAN dumps the stack trace as follows:

> =================================================================
> ==23533==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6100000002fc at pc 0x00000040507c bp 0x7ffd4aa3fa10 sp 0x7ffd4aa3fa00
> READ of size 1 at 0x6100000002fc thread T0
>     #0 0x40507b in __libdw_get_uleb128 ../libdw/memory-access.h:80
>     #1 0x496de9 in print_debug_line_section /elfutils/src/readelf.c:8846
>     #2 0x45be8c in print_debug /elfutils/src/readelf.c:11207
>     #3 0x46080a in process_elf_file /elfutils/src/readelf.c:998
>     #4 0x46080a in process_dwflmod /elfutils/src/readelf.c:760
>     #5 0x7f38d437171c in dwfl_getmodules /elfutils/libdwfl/dwfl_getmodules.c:86
>     #6 0x40cb1d in process_file /elfutils/src/readelf.c:868
>     #7 0x4059a6 in main /elfutils/src/readelf.c:350
>     #8 0x7f38d39f782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
>     #9 0x4064d8 in _start (/elfutils/build/bin/eu-readelf+0x4064d8)
> 
> 0x6100000002fc is located 0 bytes to the right of 188-byte region [0x610000000240,0x6100000002fc)
> allocated by thread T0 here:
>     #0 0x7f38d474eb90 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb90)
>     #1 0x7f38d4003f7f in convert_data /elfutils/libelf/elf_getdata.c:157
>     #2 0x7f38d4003f7f in __libelf_set_data_list_rdlock /elfutils/libelf/elf_getdata.c:447
> 
> SUMMARY: AddressSanitizer: heap-buffer-overflow ../libdw/memory-access.h:80 in __libdw_get_uleb128
> Shadow bytes around the buggy address:
>   0x0c207fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
>   0x0c207fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x0c207fff8020: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
>   0x0c207fff8030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x0c207fff8040: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
> =>0x0c207fff8050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[04]
>   0x0c207fff8060: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
>   0x0c207fff8070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x0c207fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x0c207fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>   0x0c207fff80a0: 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
> ==23533==ABORTING
Comment 1 wcventure 2019-01-22 08:59:02 UTC
Created attachment 11560 [details]
POC2
Comment 2 wcventure 2019-01-22 08:59:26 UTC
Created attachment 11561 [details]
POC3
Comment 3 Mark Wielaard 2019-01-22 17:18:30 UTC
Yeah, we should check the unknown parameters are actually there.
Luckily other checks make sure we don't read more than one byte past the end of the data.

commit a17c2c0917901ffa542ac4d3e327d46742219e04
Author: Mark Wielaard <mark@klomp.org>
Date:   Tue Jan 22 15:55:18 2019 +0100

    readelf: Don't go past end of line data reading unknown opcode parameters.
    
    https://sourceware.org/bugzilla/show_bug.cgi?id=24116
    
    Signed-off-by: Mark Wielaard <mark@klomp.org>

diff --git a/src/readelf.c b/src/readelf.c
index 6bad3bf..e3e699c 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -8845,7 +8845,8 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
                                " unknown opcode with %" PRIu8 " parameters:",
                                standard_opcode_lengths[opcode]),
                      standard_opcode_lengths[opcode]);
-             for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
+             for (int n = standard_opcode_lengths[opcode];
+                  n > 0 && linep < lineendp; --n)
                {
                  get_uleb128 (u128, linep, lineendp);
                  if (n != standard_opcode_lengths[opcode])
Comment 4 wcventure 2019-02-01 07:30:51 UTC
(In reply to Mark Wielaard from comment #3)

Not completely repaired.
Here is the Regression test case.
Comment 5 wcventure 2019-02-01 07:31:15 UTC
Created attachment 11581 [details]
Regression
Comment 6 Mark Wielaard 2019-02-01 08:12:05 UTC
(In reply to wcventure from comment #5)
> Created attachment 11581 [details]
> Regression

Running with:
 valgrind -q src/readelf --debug-dump=line ./RegressionPOC
will produce:

==57142== Invalid read of size 2
==57142==    at 0x12F431: print_debug_line_section (readelf.c:8807)
==57142==    by 0x11E2C0: print_debug (readelf.c:11212)
==57142==    by 0x1201C0: process_elf_file (readelf.c:998)
==57142==    by 0x1201C0: process_dwflmod (readelf.c:760)
==57142==    by 0x486D6A0: dwfl_getmodules (dwfl_getmodules.c:86)
==57142==    by 0x11414F: process_file (readelf.c:868)
==57142==    by 0x111C33: main (readelf.c:350)
==57142==  Address 0x4f20a83 is 0 bytes after a block of size 339 alloc'd
==57142==    at 0x483577F: malloc (vg_replace_malloc.c:299)
==57142==    by 0x48A0358: convert_data (elf_getdata.c:157)
==57142==    by 0x48A0358: __libelf_set_data_list_rdlock (elf_getdata.c:447)
==57142==    by 0x48A0547: __elf_getdata_rdlock (elf_getdata.c:554)
==57142==    by 0x484EFB0: check_section (dwarf_begin_elf.c:167)
==57142==    by 0x484F522: global_read (dwarf_begin_elf.c:310)
==57142==    by 0x484F522: dwarf_begin_elf (dwarf_begin_elf.c:445)
==57142==    by 0x486F9A7: load_dw (dwfl_module_getdwarf.c:1342)
==57142==    by 0x486FBCB: find_dw (dwfl_module_getdwarf.c:1392)
==57142==    by 0x486FBCB: dwfl_module_getdwarf (dwfl_module_getdwarf.c:1447)
==57142==    by 0x11DD4A: print_debug (readelf.c:10943)
==57142==    by 0x1201C0: process_elf_file (readelf.c:998)
==57142==    by 0x1201C0: process_dwflmod (readelf.c:760)
==57142==    by 0x486D6A0: dwfl_getmodules (dwfl_getmodules.c:86)
==57142==    by 0x11414F: process_file (readelf.c:868)
==57142==    by 0x111C33: main (readelf.c:350)
==57142== 

Fixed by:

commit cad9595592730fd8c9d0d9236d38f62ec8cfbcef
Author: Mark Wielaard <mark@klomp.org>
Date:   Fri Feb 1 09:08:14 2019 +0100

    readelf: Check there is enough data to read DWARF line opcodes arguments.
    
    When reading the debug_line opcode arguments we have to make sure there
    is enough data to read the arguments (if there are any(.
    
    The similar code in dwarf_getsrclines already had these checks.
    
    https://sourceware.org/bugzilla/show_bug.cgi?id=24116
    
    Signed-off-by: Mark Wielaard <mark@klomp.org>