Skip to content

exiv2: multiple memory safety issues

This post first appeared on oss-security.

I'm reporting three issues here in exiv2, a parser libary for image metadata. These are only examples, exiv2 is full of memory safety bugs that can trivially be found by running afl with asan for a few hours.

I have not reported those issues upstream. When I previously tried to report bugs in exiv2 found via fuzzing the upstream author made it clear to me that he has little interest in fixing those issues and doesn't consider his software suitable to parse defect files (which basically means it's unsuitable for untrusted input). The discussion can be read here. (the page is sometimes not available, searching for it in the google cache usually works though)

exiv2 is to my knowledge used by the major Linux Desktops GNOME and KDE. I'll also inform their security teams. I leave it up to Linux distros how to handle this, but it certainly is problematic that a crucial parser used by major desktop applications is not interested in fixing potential security issues.


Heap overflow (write) in tiff parser

A malformed tiff file can cause a one byte heap overflow in exiv2.

Stack trace:

==22873==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000df1 at pc 0x000000842091 bp 0x7fff51b3ee70 sp 0x7fff51b3ee68
WRITE of size 1 at 0x602000000df1 thread T0
#0 0x842090 in Exiv2::ul2Data(unsigned char*, unsigned int, Exiv2::ByteOrder) /f/exiv2-trunk/src/types.cpp:362:20
#1 0x68beac in long Exiv2::toData<unsigned int>(unsigned char*, unsigned int, Exiv2::ByteOrder) /f/exiv2-trunk/src/../include/exiv2/value.hpp:1459:16
#2 0x68beac in Exiv2::ValueType<unsigned int>::copy(unsigned char*, Exiv2::ByteOrder) const /f/exiv2-trunk/src/../include/exiv2/value.hpp:1612
#3 0x6742b2 in Exiv2::Exifdatum::copy(unsigned char*, Exiv2::ByteOrder) const /f/exiv2-trunk/src/exif.cpp:362:48
#4 0x7f794d in Exiv2::TiffImage::readMetadata() /f/exiv2-trunk/src/tiffimage.cpp:204:18
#5 0x59786a in Action::Print::printSummary() /f/exiv2-trunk/src/actions.cpp:289:16
#6 0x596ef8 in Action::Print::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /f/exiv2-trunk/src/actions.cpp:244:44
#7 0x55fb3f in main /f/exiv2-trunk/src/exiv2.cpp:170:25
#8 0x7f91c1e571d0 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.24-r2/work/glibc-2.24/csu/../csu/libc-start.c:289
#9 0x468979 in _start (/r/exiv2/exiv2+0x468979)

0x602000000df1 is located 0 bytes to the right of 1-byte region [0x602000000df0,0x602000000df1)
allocated by thread T0 here:
#0 0x55af00 in operator new[](unsigned long) (/r/exiv2/exiv2+0x55af00)
#1 0x83fadf in Exiv2::DataBuf::alloc(long) /f/exiv2-trunk/src/types.cpp:158:22



Heap out of bounds read in jp2 / JPEG2000 parser

A malformed jpeg2000 file causes a (large) out of bounds read.

Stack trace:

==32038==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f5e099e6838 at pc 0x00000050f22c bp 0x7ffdf7f3dcd0 sp 0x7ffdf7f3d480
READ of size 808464432 at 0x7f5e099e6838 thread T0
#0 0x50f22b in __asan_memcpy (/r/exiv2/exiv2+0x50f22b)
#1 0x6e82bc in Exiv2::Jp2Image::readMetadata() /f/exiv2-trunk/src/jp2image.cpp:277:29
#2 0x59786a in Action::Print::printSummary() /f/exiv2-trunk/src/actions.cpp:289:16
#3 0x596ef8 in Action::Print::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /f/exiv2-trunk/src/actions.cpp:244:44
#4 0x55fb3f in main /f/exiv2-trunk/src/exiv2.cpp:170:25
#5 0x7f5e130a71d0 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.24-r2/work/glibc-2.24/csu/../csu/libc-start.c:289
#6 0x468979 in _start (/r/exiv2/exiv2+0x468979)

0x7f5e099e6838 is located 0 bytes to the right of 808452152-byte region [0x7f5dd96e6800,0x7f5e099e6838)
allocated by thread T0 here:
#0 0x55af00 in operator new[](unsigned long) (/r/exiv2/exiv2+0x55af00)
#1 0x6e8176 in Exiv2::DataBuf::DataBuf(long) /f/exiv2-trunk/src/../include/exiv2/types.hpp:204:46
#2 0x6e8176 in Exiv2::Jp2Image::readMetadata() /f/exiv2-trunk/src/jp2image.cpp:273
#3 0x59786a in Action::Print::printSummary() /f/exiv2-trunk/src/actions.cpp:289:16
#4 0x596ef8 in Action::Print::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /f/exiv2-trunk/src/actions.cpp:244:44
#5 0x7f5e130a71d0 in
__libc_start_main /var/tmp/portage/sys-libs/glibc-2.24-r2/work/glibc-2.24/csu/../csu/libc-start.c:289



Stack out of bounds read in webp parser

A malformed webp file causes a six bytes stack out of bounds read.

Stack trace:

==598==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc12aa054 at pc 0x0000004fe311 bp 0x7ffcc12a9f90 sp 0x7ffcc12a9740
READ of size 6 at 0x7ffcc12aa054 thread T0
#0 0x4fe310 in __interceptor_memcmp.part.76 (/r/exiv2/exiv2+0x4fe310)
#1 0x8889d0 in Exiv2::WebPImage::getHeaderOffset(unsigned char*, long, unsigned char*, long) /f/exiv2-trunk/src/webpimage.cpp:798:17
#2 0x8889d0 in Exiv2::WebPImage::decodeChunks(unsigned long) /f/exiv2-trunk/src/webpimage.cpp:601
#3 0x884ff2 in Exiv2::WebPImage::readMetadata() /f/exiv2-trunk/src/webpimage.cpp:496:20
#4 0x59786a in Action::Print::printSummary() /f/exiv2-trunk/src/actions.cpp:289:16
#5 0x596ef8 in Action::Print::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /f/exiv2-trunk/src/actions.cpp:244:44
#6 0x55fb3f in main /f/exiv2-trunk/src/exiv2.cpp:170:25
#7 0x7f7f9cc9f1d0 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.24-r2/work/glibc-2.24/csu/../csu/libc-start.c:289
#8 0x468979 in _start (/r/exiv2/exiv2+0x468979)

Address 0x7ffcc12aa054 is located in stack of thread T0 at offset 180 in frame
#0 0x885a0f in Exiv2::WebPImage::decodeChunks(unsigned long) /f/exiv2-trunk/src/webpimage.cpp:501

This frame has 13 object(s):
[32, 36) 'size_buff' (line 503)
[48, 64) 'payload' (line 516)
[80, 84) 'size_buf' (line 520)
[96, 100) 'size_buf48' (line 536)
[112, 114) 'size_buf_w' (line 551)
[128, 131) 'size_buf_h' (line 552)
[144, 148) 'size_buf112' (line 568)
[160, 162) 'size_buff152' (line 587)
[176, 180) 'exifLongHeader295' (line 588) <== Memory access at offset 180 overflows this variable
[192, 196) 'exifTiffBEHeader297' (line 591)
[208, 272) 'xmpData' (line 650)
[304, 688) 'temp.lvalue'
[752, 1136) 'temp.lvalue232'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions are supported)