hzeller / timg

A terminal image and video viewer.
GNU General Public License v2.0
1.91k stars 73 forks source link

Detected Crash: AddressSanitizer: heap-buffer-overflow #115

Closed liode1s closed 1 year ago

liode1s commented 1 year ago

I use afl++ fuzzing this program

export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
cmake -B build && cmake --build build
➜  src ./timg --version
timg 1.5.1+  <https://timg.sh/>
Copyright (c) 2016..2023 Henner Zeller. This program is free software; license GPL 2.0.

Image decoding GraphicsMagick 1.3.38 (2022-03-26)
Turbo JPEG
QOI Image reader
STB image loading fallback
Video decoding libav 58.76.100
Half, quarter, iterm2, and kitty encoding timg builtin.
Libsixel version 1.8.6

then build afl++ run this poc

=================================================================
==2368346==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61200000045c at pc 0x00000051d18e bp 0x7fff288903e0 sp 0x7fff288903d8
WRITE of size 4 at 0x61200000045c thread T0
    #0 0x51d18d in void timg::StoreBacking<2>(timg::rgba_t*, timg::rgba_t const*, timg::rgba_t const*) /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:145:20
    #1 0x51d18d in char* timg::UnicodeBlockCanvas::AppendDoubleRow<2, 24>(char*, int, int, timg::rgba_t const*, timg::rgba_t const*, bool, int*) /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:305:9
    #2 0x518011 in timg::UnicodeBlockCanvas::Send(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration) /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:374:23
    #3 0x5126c6 in timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)::operator()(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&) const /home/xxx/timg-1.5.1/src/renderer.cc:50:22
    #4 0x5126c6 in void std::__invoke_impl<void, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration>(std::__invoke_other, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:61:14
    #5 0x5126c6 in std::enable_if<__and_<std::is_void<void>, std::__is_invocable<timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration> >::value, void>::type std::__invoke_r<void, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration>(timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:154:7
    #6 0x5126c6 in std::_Function_handler<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration), timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)>::_M_invoke(std::_Any_data const&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_function.h:290:9
    #7 0x5730d7 in std::function<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration)>::operator()(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration) const /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_function.h:590:9
    #8 0x5730d7 in timg::STBImageSource::SendFrames(timg::Duration const&, int, int const volatile&, std::function<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration)> const&) /home/xxx/timg-1.5.1/src/stb-image-source.cc:163:13
    #9 0x4e10eb in PresentImages(std::vector<std::future<timg::ImageSource*>, std::allocator<std::future<timg::ImageSource*> > >*, timg::DisplayOptions const&, timg::PresentationOptions const&, timg::BufferedWriteSequencer*, bool*) /home/xxx/timg-1.5.1/src/timg.cc:373:17
    #10 0x4e10eb in main /home/xxx/timg-1.5.1/src/timg.cc:960:5
    #11 0x7fa43c829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #12 0x7fa43c829e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #13 0x426ee4 in _start (/home/xxx/timg-1.5.1/afl/src/timg+0x426ee4)

0x61200000045c is located 0 bytes to the right of 284-byte region [0x612000000340,0x61200000045c)
allocated by thread T0 here:
    #0 0x4a40c3 in __interceptor_realloc (/home/xxx/timg-1.5.1/afl/src/timg+0x4a40c3)
    #1 0x5179a8 in timg::UnicodeBlockCanvas::RequestBuffers(int, int) /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:424:42
    #2 0x5179a8 in timg::UnicodeBlockCanvas::Send(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration) /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:322:26
    #3 0x5126c6 in timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)::operator()(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&) const /home/xxx/timg-1.5.1/src/renderer.cc:50:22
    #4 0x5126c6 in void std::__invoke_impl<void, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration>(std::__invoke_other, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:61:14
    #5 0x5126c6 in std::enable_if<__and_<std::is_void<void>, std::__is_invocable<timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration> >::value, void>::type std::__invoke_r<void, timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration>(timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:154:7
    #6 0x5126c6 in std::_Function_handler<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration), timg::(anonymous namespace)::SingleColumnRenderer::render_cb(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::'lambda'(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration const&)>::_M_invoke(std::_Any_data const&, int&&, int&&, timg::Framebuffer const&, timg::SeqType&&, timg::Duration&&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_function.h:290:9
    #7 0x5730d7 in std::function<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration)>::operator()(int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration) const /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_function.h:590:9
    #8 0x5730d7 in timg::STBImageSource::SendFrames(timg::Duration const&, int, int const volatile&, std::function<void (int, int, timg::Framebuffer const&, timg::SeqType, timg::Duration)> const&) /home/xxx/timg-1.5.1/src/stb-image-source.cc:163:13
    #9 0x4e10eb in PresentImages(std::vector<std::future<timg::ImageSource*>, std::allocator<std::future<timg::ImageSource*> > >*, timg::DisplayOptions const&, timg::PresentationOptions const&, timg::BufferedWriteSequencer*, bool*) /home/xxx/timg-1.5.1/src/timg.cc:373:17
    #10 0x4e10eb in main /home/xxx/timg-1.5.1/src/timg.cc:960:5
    #11 0x7fa43c829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/xxx/timg-1.5.1/src/unicode-block-canvas.cc:145:20 in void timg::StoreBacking<2>(timg::rgba_t*, timg::rgba_t const*, timg::rgba_t const*)
Shadow bytes around the buggy address:
  0x0c247fff8030: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c247fff8040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fff8050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa
  0x0c247fff8060: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c247fff8070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c247fff8080: 00 00 00 00 00 00 00 00 00 00 00[04]fa fa fa fa
  0x0c247fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fff80a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fff80b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c247fff80d0: 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
==2368346==ABORTING
hzeller commented 1 year ago

Can you try again from head ?

liode1s commented 1 year ago

Of course, I use the timg-2e9414e668144bbe0afc074dac17b74ef4acfdcf branch to compile and run the fuzz seed. There is no crash behavior.

/timg ~/Desktop/sixel/output1/crashes/2023-08-16_15:48:42_0:30:07.094816_CLI-1-AFLPP_b439ea3b32235899c6d7f67332025a82.cov 

▗

Thanks . Then Can I request a CVE ?

hzeller commented 1 year ago

Fixed in v1.5.2 If you think it can be exploited, file a CVE and point to this bug and the new version as containing the fix.

coldtobi commented 1 year ago

According to the Debian Security Team, this has been assigned CVE-2023-40968

hzeller commented 1 year ago

Thanks Tobi. Updated the 1.5.2 release to contain a link to upstream https://nvd.nist.gov/vuln/detail/CVE-2023-40968

Unfortunately, the original CVE mentioned 1.5.2 of being vulnerable, even though ti is the one fixing it.

coldtobi commented 1 year ago

It seems that the CVE has been registered through MITRE, so they might be able to correct the version information: https://cveform.mitre.org/

hzeller commented 1 year ago

Thanks for the link. I've submitted a request to change the description to mention v1.5.1 and before instead.