Talos Vulnerability Report

TALOS-2022-1643

OpenImageIO TIFF IPTC decoding information disclosure vulnerability

December 22, 2022
CVE Number

CVE-2022-41988

SUMMARY

An information disclosure vulnerability exists in the OpenImageIO::decode_iptc_iim() functionality of OpenImageIO Project OpenImageIO v2.3.19.0. A specially-crafted TIFF file can lead to a disclosure of sensitive information. An attacker can provide a malicious file to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

OpenImageIO Project OpenImageIO v2.3.19.0

PRODUCT URLS

OpenImageIO - https://github.com/OpenImageIO/oiio

CVSSv3 SCORE

5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

CWE

CWE-125 - Out-of-bounds Read

DETAILS

OpenImageIO is an image processing library with easy-to-use interfaces and a sizable number of supported image formats. Useful for conversion and processing and even image comparison, this library is utilized by 3D-processing software from AliceVision (including Meshroom), as well as Blender for reading Photoshop .psd files.

With OpenImageIO, handling of different file formats is a relatively simple matter. auto inp = ImageInput::open(filename) takes care of all the intricate details of figuring out which file format we’re dealing with and redirects the codeflow to the appropriate file handlers. For the TIFF file format, we first end up hitting the TIFFOpen function inside of the opensource LibTIFF library, which provides a parsed TIFF object to OpenImageIO inside of TIFFInput:seek_subimage:

bool
TIFFInput::seek_subimage(int subimage, int miplevel)
{
 // [...]
    if (!m_tif) {
        if (ioproxy_opened()) {
            static_assert(sizeof(thandle_t) == sizeof(void*),
                          "thandle_t must be same size as void*");
            // Strutil::print("\n\nOpening client \"{}\"\n", m_filename);
            ioseek(0);
            m_tif = TIFFClientOpen(m_filename.c_str(), "rm", ioproxy(),
                                   reader_readproc, reader_writeproc,
                                   reader_seekproc, reader_closeproc,
                                   reader_sizeproc, reader_mapproc,
                                   reader_unmapproc);
        } else {
#ifdef _WIN32
            std::wstring wfilename = Strutil::utf8_to_utf16wstring(m_filename);
            m_tif                  = TIFFOpenW(wfilename.c_str(), "rm");
#else
            m_tif = TIFFOpen(m_filename.c_str(), "rm");  // [1]
#endif
        }

We first hit the code path at [1] since we don’t have a m_tif object yet, which eventually gets us to the lengthy LibTIFF file parsing inside TIFFReadDirectory. For brevity’s sake, instead of going through the code we will just examine a sample hexdump to explain the file format:

00000000   49 49 2A 00  08 00 00 00  18 00 00 01  03 00 01 00  II*............. // [2]
00000010   00 00 80 00  00 00 01 01  03 00 01 00  00 00 00 02  ................
00000020   00 00 02 01  03 00 03 00  00 00 2E 01  00 00 03 01  ................
00000030   03 00 01 00  00 00 08 00  00 00 06 01  03 00 01 00  ................
00000040   00 00 02 00  00 00 12 01  03 00 01 00  00 00 01 00  ................
00000050   00 00 15 01  03 00 01 00  00 00 03 00  00 00 16 01  ................
00000060   03 00 01 00  00 00 20 00  00 00 1C 01  03 00 01 00  ...... .........
00000070   00 00 01 00  00 00 1E 01  05 00 01 00  00 00 34 01  ..............4.
00000080   00 00 1F 01  05 00 01 00  00 00 3C 01  00 00 31 01  ..........<...1.
00000090   02 00 23 00  00 00 44 01  00 00 32 01  02 00 14 00  ..#...D...2.....
000000A0   00 00 68 01  00 00 3D 01  03 00 01 00  00 00 02 00  ..h...=.........
000000B0   00 00 42 01  03 00 01 00  00 00 40 00  00 00 43 01  ..B.......@...C.
000000C0   03 00 01 00  00 00 40 00  00 00 44 01  04 00 10 00  ......@...D.....
000000D0   00 00 7C 01  00 00 45 01  04 00 10 00  00 00 BC 01  ..|...E.........
000000E0   00 00 53 01  03 00 03 00  00 00 FC 01  00 00 BC 02  ..S.............
000000F0   01 00 24 02  00 00 02 02  00 00 16 82  02 00 0E 00  ..$.............
00000100   00 00 26 04  00 00 17 82  02 00 0C 00  00 00 34 04  ..&...........4.
00000110   00 00 18 82  0B 00 01 00  00 00 00 00  80 3E BB 83  .............>..
00000120   04 00 0A 00  00 00 40 04  00 00 F8 09  00 00 08 00  ......@.........


#define TIFF_BIGENDIAN      0x4d4d
#define TIFF_LITTLEENDIAN   0x4949
#define MDI_LITTLEENDIAN    0x5045
#define MDI_BIGENDIAN       0x4550


typedef struct {
    uint16_t tiff_magic;      /* magic number (defines byte order) */
    uint16_t tiff_version;    /* TIFF version number */
    uint32_t tiff_diroff;     /* byte offset to first directory */
} TIFFHeaderClassic; // [3]

typedef struct {
    uint16_t tiff_magic;      /* magic number (defines byte order) */
    uint16_t tiff_version;    /* TIFF version number */
    uint16_t tiff_offsetsize; /* size of offsets, should be 8 */
    uint16_t tiff_unused;     /* unused word, should be 0 */
    uint64_t tiff_diroff;     /* byte offset to first directory */
} TIFFHeaderBig;     // [4]

Since our first 2 bytes match the TIFF_LITTLEENDIAN header of 0x4949 [2], we deal with the TiffHeaderClassic[3] instead of the TIFFHeaderBig [4]. Thus, the uint32_t at offset 0x4 is our tiff_diroff, i.e. the offset at which we find our tiff directories (0x8). TIFFReadDirectory then goes to that offset and reads in the number of directories we have. In this case, at offset 0x8 at [2], we can see that our uint16_t dircount is 0x18. Since we are not dealing with a TIFFHeaderBig, each of these “directories” is 0xC bytes long and looks as such:

[-.-]> ptype TIFFDirEntry
type = struct TIFFDirEntry {
    uint16_t tdir_tag;
    uint16_t tdir_type;
    uint32_t tdir_count;
    uint32_t tdir_offset;
}

As such, TiffReadDirectory reads in the dircount16 * dirsize bytes immediately following our dircount. In our case, this would be 0x18 * 0xc, resulting in 0x120 bytes being read. In the hexdump above, this would mean that our directories come directly from the bytes at offset (0xA, 0x12A). For example, if we look at the directories at offset 0x8E get parsed, we see the following:

00000080                                             31 01  |..........<...1.|
00000090  02 00 23 00 00 00 44 01  00 00 // start of second directory
                                         32 01 02 00 14 00  |..#...D...2.....|
000000a0  00 00 68 01 00 00

[^~^]> p/x *direntry
$45 = {tdir_tag = 0x131, tdir_type = 0x2, tdir_count = 0x23, tdir_offset = {toff_short = 0x144, toff_long = 0x144, toff_long8 = 0x144}, tdir_ignore = 0x0}

[^.^]> p/x *(direntry+1)
$46 = {tdir_tag = 0x132, tdir_type = 0x2, tdir_count = 0x14, tdir_offset = {toff_short = 0x168, toff_long = 0x168, toff_long8 = 0x168}, tdir_ignore = 0x0}

The tdir_tag field is used to sort the array of directories, along with identifying the data. The tdir_type field designates the data’s type and the tdir_count designates the length of the data, then finally the tdir_offset field points to the offset inside the TIFF file where we can actually find the data. So when LibOpenImageIO wants to find the data for a given tag, it first consults the hardcoded LibTiff array of possible field types and then binary searches through the directories that it has loaded in from the file. A sample of predefined LibTiff fields is given below:

static const TIFFField
tiffFields[] = {
    { TIFFTAG_SUBFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "SubfileType", NULL },
    { TIFFTAG_OSUBFILETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "OldSubfileType", NULL },
    { TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 0, 0, "ImageWidth", NULL },
    { TIFFTAG_IMAGELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 1, 0, "ImageLength", NULL },
    // [...]
}    

These directories are for the most part utilized inside of the void TIFFInput::readspec(bool read_meta) function, which calls the various TIFFGetField function to grab the needed data from each directory. In the most basic case that looks like so:

void
TIFFInput::readspec(bool read_meta)
{
    uint32_t width = 0, height = 0, depth = 0;
    TIFFGetField(m_tif, TIFFTAG_IMAGEWIDTH, &width);
    TIFFGetField(m_tif, TIFFTAG_IMAGELENGTH, &height);
    TIFFGetFieldDefaulted(m_tif, TIFFTAG_IMAGEDEPTH, &depth);
    TIFFGetFieldDefaulted(m_tif, TIFFTAG_SAMPLESPERPIXEL, &m_inputchannels);
    // [...] 

Much further down inside of void TIFFInput::readspec(bool read_meta) we see a parsing of the TIFFTAG_RICHTIFFIPTC directory, where the tag is 0x83bb:

    int iptcsize         = 0;
    const void* iptcdata = NULL;
    if (TIFFGetField(m_tif, TIFFTAG_RICHTIFFIPTC, &iptcsize, &iptcdata)) { // [3]
        std::vector<uint32_t> iptc((uint32_t*)iptcdata,   // [4]
                                   (uint32_t*)iptcdata + iptcsize);
        if (TIFFIsByteSwapped(m_tif))
            TIFFSwabArrayOfLong((uint32_t*)&iptc[0], iptcsize);
        decode_iptc_iim(&iptc[0], iptcsize * 4, m_spec);  // [5]
    }

The data is pulled from the directory and thrown into the iptcsize and iptcdata variables at [3], and then a uint32_t vector is created with that data at [4], via the (start_ptr, end_ptr ) initializer. Now, quickly going back to our input hexdump, let’s find what the TIFFTAG_RICHTIFFIPTC looks like:

#define TIFFTAG_RICHTIFFIPTC        33723 (0x83bb)

0000020a    BB 83  04 00 0A 00  00 00 40 04 // [6]
// [...]

As shown above at [6], we see the TIFFTAG_RICHTIFFIPTC tag at offset 0x20a, followed by the tdir_type, tdir_count, and tdir_offset. Thus, the type of the data is TIFF_LONG, there are 0xa entries, and the data starts at offset 0x440. So if we look at offset 0x440, we can see the following:

00000440   59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  Y...Y...Y...Y...Y...Y...
00000458   59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  59 00 00 00  Y...Y...Y...Y...Y...Y...
00000470   59 00 00 00  59 00 00 00  01 00 01 00  01 00 3C 3F  78 70 61 63  6B 65 74 20  Y...Y.........

And this sample data corresponds to what we see in a debugger inside of iptcdata and iptcsize after TIFFGetField [3] finishes:

[x.x]> x/1s iptcdata
0x602000000270: "YYYYYYYYYY"

[^~^]> p/x iptcsize
$8 = 0xa

To summarize, we control the size and contents of the iptc vector generated at [4], and this is then passed into the decode_iptc_iim function at [5]:

bool
decode_iptc_iim(const void* iptc, int length, ImageSpec& spec)
{
    const unsigned char* buf = (const unsigned char*)iptc;

    // Now there are a series of data blocks.  Each one starts with 1C
    // 02, then a single byte indicating the tag type, then 2 byte (big
    // endian) giving the tag length, then the data itself.  This
    // repeats until we've used up the whole segment buffer, or I guess
    // until we don't find another 1C 02 tag start.
    // N.B. I don't know why, but Picasa sometimes uses 1C 01 !
    while (length > 0 && buf[0] == 0x1c && (buf[1] == 0x02 || buf[1] == 0x01)) { // [7]
        int secondbyte = buf[1];
        int tagtype    = buf[2];
        int tagsize    = (buf[3] << 8) + buf[4];                // [8]
        buf += 5;
        length -= 5;

        if (secondbyte == 0x02) {
            std::string s((const char*)buf, tagsize);             // [9] 

            for (int i = 0; iimtag[i].name; ++i) {
                if (tagtype == iimtag[i].tag) {
                    if (iimtag[i].repeatable) {
                        // For repeatable IIM tags, concatenate them
                        // together separated by semicolons
                        s               = Strutil::strip(s);
                        std::string old = spec.get_string_attribute(
                            iimtag[i].name);
                        if (old.size())
                            old += "; ";
                        spec.attribute(iimtag[i].name, old + s);
                    } else {
                        spec.attribute(iimtag[i].name, s); 
                    }
                    if (iimtag[i].anothername)
                        spec.attribute(iimtag[i].anothername, s);
                    break;
                }
            }
        }

        buf += tagsize;
        length -= tagsize;
    }

    return true;
}

Assuming our input bytes pass the validation at [7], we create a new std::string s at [9], where the length is controlled by the input file and can be up to 0xFFFF. Since there’s no validation between [8] and [9], and also because our input data was read into a vector at [4], this ends up being a decent out-of-bounds heap read, where the data gets stored into our resultant m_spec object at one of the calls to spec.attribute within the function.

It’s worth noting that this vulnerability has been fixed as of release v2.4.4.2 and in the master branch (https://github.com/OpenImageIO/oiio/commit/e9103925bb2aeed36b01b3805f36959f5d1a2e18#diff-8496b368a265f99b41e3c06bf99a5ea82d4f40fff1919ee79caa26ae033b3a06R118).

Crash Information

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2021219380
INFO: Loaded 3 modules   (1146257 inline 8-bit counters): 102986 [0x7f643eaad0c0, 0x7f643eac630a), 1043122 [0x7f644cd27240, 0x7f644ce25cf2), 149 [0x55e76230d108, 0x55e76230d19d), 
INFO: Loaded 3 PC tables (1146257 PCs): 102986 [0x7f643eac6310,0x7f643ec587b0), 1043122 [0x7f644ce25cf8,0x7f644de10818), 149 [0x55e76230d1a0,0x55e76230daf0), 
./fuzz_oiio.bin: Running 1 inputs 1 time(s) each.
Running: ./crash-436f62e9d1e9692954d02c72d04c47952574f294
=================================================================
==61746==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000298 at pc 0x55e76221ff64 bp 0x7ffe6f9a5060 sp 0x7ffe6f9a4828
READ of size 34 at 0x602000000298 thread T0
    #0 0x55e76221ff63 in __interceptor_memcpy (/oiio/fuzzing/fuzz_oiio.bin+0x74f63) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #1 0x7f644df65c63 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) (/lib/x86_64-linux-gnu/libstdc++.so.6+0x14ec63) (BuildId: f57e02bfadacc0c923c82457d5e18e1830b5faea)
    #2 0x7f6448736aba in OpenImageIO_v2_3::decode_iptc_iim(void const*, int, OpenImageIO_v2_3::ImageSpec&) /oiio/oiio-2.3.19.0/src/libOpenImageIO/iptc.cpp:129:25
    #3 0x7f64497c6185 in OpenImageIO_v2_3::TIFFInput::readspec(bool) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:1241:9
    #4 0x7f64497ab9eb in OpenImageIO_v2_3::TIFFInput::seek_subimage(int, int) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:778:9
    #5 0x7f64497a9158 in OpenImageIO_v2_3::TIFFInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec&) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:690:15
    #6 0x7f64497b073c in OpenImageIO_v2_3::TIFFInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec&, OpenImageIO_v2_3::ImageSpec const&) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:716:12
    #7 0x7f64486b6859 in OpenImageIO_v2_3::ImageInput::create(OpenImageIO_v2_3::string_view, bool, OpenImageIO_v2_3::ImageSpec const*, OpenImageIO_v2_3::Filesystem::IOProxy*, OpenImageIO_v2_3::string_view) /oiio/oiio-2.3.19.0/src/libOpenImageIO/imageioplugin.cpp:758:27
    #8 0x7f6448595699 in OpenImageIO_v2_3::ImageInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec const*, OpenImageIO_v2_3::Filesystem::IOProxy*) /oiio/oiio-2.3.19.0/src/libOpenImageIO/imageinput.cpp:105:16
    #9 0x55e7622c640f in LLVMFuzzerTestOneInput /oiio/fuzzing/./oiio_harness.cpp:77:16
    #10 0x55e7621ec4e3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/oiio/fuzzing/fuzz_oiio.bin+0x414e3) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #11 0x55e7621d625f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/oiio/fuzzing/fuzz_oiio.bin+0x2b25f) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #12 0x55e7621dbfb6 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/oiio/fuzzing/fuzz_oiio.bin+0x30fb6) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #13 0x55e762205dd2 in main (/oiio/fuzzing/fuzz_oiio.bin+0x5add2) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #14 0x7f643ecc9d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #15 0x7f643ecc9e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #16 0x55e7621d0b24 in _start (/oiio/fuzzing/fuzz_oiio.bin+0x25b24) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)

0x602000000298 is located 0 bytes to the right of 8-byte region [0x602000000290,0x602000000298)
allocated by thread T0 here:
    #0 0x55e7622c390d in operator new(unsigned long) (/oiio/fuzzing/fuzz_oiio.bin+0x11890d) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #1 0x7f644354ccad in __gnu_cxx::new_allocator<unsigned int>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:127:27
    #2 0x7f644354c6b3 in std::allocator_traits<std::allocator<unsigned int> >::allocate(std::allocator<unsigned int>&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:464:20
    #3 0x7f644592084b in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:346:20
    #4 0x7f6449829d69 in void std::vector<unsigned int, std::allocator<unsigned int> >::_M_range_initialize<unsigned int*>(unsigned int*, unsigned int*, std::forward_iterator_tag) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:1582:14
    #5 0x7f64497d8b16 in std::vector<unsigned int, std::allocator<unsigned int> >::vector<unsigned int*, void>(unsigned int*, unsigned int*, std::allocator<unsigned int> const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:657:4
    #6 0x7f64497c5c5d in OpenImageIO_v2_3::TIFFInput::readspec(bool) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:1237:31
    #7 0x7f64497ab9eb in OpenImageIO_v2_3::TIFFInput::seek_subimage(int, int) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:778:9
    #8 0x7f64497a9158 in OpenImageIO_v2_3::TIFFInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec&) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:690:15
    #9 0x7f64497b073c in OpenImageIO_v2_3::TIFFInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec&, OpenImageIO_v2_3::ImageSpec const&) /oiio/oiio-2.3.19.0/src/tiff.imageio/tiffinput.cpp:716:12
    #10 0x7f64486b6859 in OpenImageIO_v2_3::ImageInput::create(OpenImageIO_v2_3::string_view, bool, OpenImageIO_v2_3::ImageSpec const*, OpenImageIO_v2_3::Filesystem::IOProxy*, OpenImageIO_v2_3::string_view) /oiio/oiio-2.3.19.0/src/libOpenImageIO/imageioplugin.cpp:758:27
    #11 0x7f6448595699 in OpenImageIO_v2_3::ImageInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_3::ImageSpec const*, OpenImageIO_v2_3::Filesystem::IOProxy*) /oiio/oiio-2.3.19.0/src/libOpenImageIO/imageinput.cpp:105:16
    #12 0x55e7622c640f in LLVMFuzzerTestOneInput /oiio/fuzzing/./oiio_harness.cpp:77:16
    #13 0x55e7621ec4e3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/oiio/fuzzing/fuzz_oiio.bin+0x414e3) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #14 0x55e7621d625f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/oiio/fuzzing/fuzz_oiio.bin+0x2b25f) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #15 0x55e7621dbfb6 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/oiio/fuzzing/fuzz_oiio.bin+0x30fb6) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #16 0x55e762205dd2 in main (/oiio/fuzzing/fuzz_oiio.bin+0x5add2) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25)
    #17 0x7f643ecc9d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow (/oiio/fuzzing/fuzz_oiio.bin+0x74f63) (BuildId: e9d97e110da8ca7129ca0569fb37dfa703dccc25) in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x0c047fff8000: fa fa fd fa fa fa fd fd fa fa fd fa fa fa fd fa
  0x0c047fff8010: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
  0x0c047fff8020: fa fa fd fd fa fa fd fd fa fa 00 00 fa fa fd fa
  0x0c047fff8030: fa fa fd fa fa fa fd fd fa fa 00 06 fa fa fd fd
  0x0c047fff8040: fa fa 00 04 fa fa 04 fa fa fa fd fd fa fa 00 02
=>0x0c047fff8050: fa fa 00[fa]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff80a0: 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
==61746==ABORTING
TIMELINE

2022-10-19 - Initial Vendor Contact
2022-10-20 - Vendor Disclosure
2022-11-01 - Vendor Patch Release
2022-12-22 - Public Release

Credit

Discovered by Lilith >_> of Cisco Talos.