Skip to content

bug: heap-buffer-overflow (out-of-bounds write) in TIFFInput::bit_convert (src/tiff.imageio/tiffinput.cpp) #5315

Description

@beilzx

Describe the bug
OpenImageIO's TIFF reader contains a heap-based buffer overflow (out-of-bounds write) in TIFFInput::bit_convert(), reachable when parsing a crafted tiled TIFF image whose PhotometricInterpretation is Separated/CMYK (4 samples per pixel) combined with a BitsPerSample value less than 8.
When such an image is read, OpenImageIO converts the CMYK data to a 3-channel RGB output internally, but the buffer allocated for the decoded tile is sized according to the number of channels in the file (4), while the unpacking routine that expands the sub-8-bit samples writes based on the reduced output channel count (3). This inconsistency between the buffer's actual size and the number of values written into it results in the write loop running past the end of the allocated buffer, corrupting adjacent heap memory.
The vulnerability is triggered simply by requesting any pixel data or statistics from the crafted file (e.g. via iinfo --stats, iinfo --hash, or iconvert), i.e. any normal use of the library that reads image content rather than just metadata.

OpenImageIO version and dependencies
OIIO 3.2.0.1dev | Linux/x86_64
Build compiler: clang 19.1 | C++17/201703
HW features enabled at build: sse2
No CUDA support (disabled / unavailable at build time)
Dependencies: DCMTK NONE, FFmpeg NONE, fmt 12.1.0, Freetype NONE, GIF NONE, Imath 3.1.9, JPEG 80, JXL NONE,
Libheif NONE, libjpeg-turbo NONE, LibRaw NONE, libuhdr NONE, OpenColorIO 2.4.0, OpenCV NONE, OpenEXR 3.1.5,
OpenJPEG NONE, openjph NONE, PNG 1.6.43, Ptex NONE, Ptex NONE, Robinmap 1.4.0, TBB NONE, TIFF 4.5.1, WebP
1.3.2, ZLIB 1.3

To Reproduce
Steps to reproduce the behavior:

git clone https://github.com/AcademySoftwareFoundation/OpenImageIO.git
cd OpenImageIO

make nuke

export CC=clang
export CXX=clang++

export CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g"
export CXXFLAGS="-fsanitize=address -fno-omit-frame-pointer -g"
export LDFLAGS="-fsanitize=address"

make STOP_ON_WARNING=0 \
     USE_OPENGL=0 \
     USE_QT=0 \
     USE_PYTHON=0 \
     BUILD_SHARED_LIBS=1 \
     LINKSTATIC=0 \
     -j$(nproc)

./dist/bin/iinfo --stats poc.tif

Evidence
AddressSanitizer crash report:

/src/test/results/fuzzing_openimageio/test/cnvd_report/POC1_tiff_heap_overflow.tif :    1 x 4096, 3 channel, uint1 tiff
=================================================================
==144981==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x527000003100 at pc 0x7fc024ddecf4 bp 0x7fffc2c11170 sp 0x7fffc2c11168
WRITE of size 1 at 0x527000003100 thread T0
    #0 0x7fc024ddecf3 in OpenImageIO::v3_2_0::TIFFInput::bit_convert(int, unsigned char const*, int, void*, int) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:1852:38
    #1 0x7fc024ddecf3 in OpenImageIO::v3_2_0::TIFFInput::read_native_tile_locked(int, int, int, int, int, OpenImageIO::v3_1::span<std::byte, 18446744073709551615ul>) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2450:17
    #2 0x7fc024ddefbb in OpenImageIO::v3_2_0::TIFFInput::read_native_tile(int, int, int, int, int, void*) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2493:12
    #3 0x7fc024a3cb42 in OpenImageIO::v3_1::ImageInput::read_tile(int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:645:15
    #4 0x7fc024de23bb in OpenImageIO::v3_2_0::TIFFInput::read_tile(int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2790:27
    #5 0x7fc024a3eacc in OpenImageIO::v3_1::ImageInput::read_tiles(int, int, int, int, int, int, int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:843:31
    #6 0x7fc024de26b4 in OpenImageIO::v3_2_0::TIFFInput::read_tiles(int, int, int, int, int, int, int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2821:27
    #7 0x7fc024a42319 in OpenImageIO::v3_1::ImageInput::read_image(int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long, bool (*)(void*, float), void*) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:1150:23
    #8 0x7fc024923b16 in OpenImageIO::v3_1::ImageBufImpl::read(int, int, int, int, bool, OpenImageIO::v3_1::TypeDesc, bool (*)(void*, float), void*, OpenImageIO::v3_2_0::DoLock) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imagebuf.cpp:1465:27
    #9 0x7fc02492b5ca in OpenImageIO::v3_1::ImageBuf::read(int, int, bool, OpenImageIO::v3_1::TypeDesc, bool (*)(void*, float), void*) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imagebuf.cpp:1528:20
    #10 0x55d87f997512 in read_input(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, OpenImageIO::v3_1::ImageBuf&, int, int) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:64:13
    #11 0x55d87f997512 in print_stats(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, OpenImageIO::v3_1::ImageSpec const&, int, int, bool) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:81:10
    #12 0x55d87f997512 in print_info_subimage(int, int, OpenImageIO::v3_1::ImageSpec&, OpenImageIO::v3_1::ImageInput*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:316:18
    #13 0x55d87f997512 in print_info(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned long, OpenImageIO::v3_1::ImageInput*, OpenImageIO::v3_1::ImageSpec&, bool, bool, long long&) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:421:14
    #14 0x55d87f990c32 in main /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:492:14
    #15 0x7fc023a7f1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #16 0x7fc023a7f28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #17 0x55d87f8acad4 in _start (/src/test/results/fuzzing_openimageio/test/OpenImageIO/dist/bin/iinfo+0x3aad4) (BuildId: b43058b69e4695b86490b4cc450eda2919868e7c)

0x527000003100 is located 0 bytes after 12288-byte region [0x527000000100,0x527000003100)
allocated by thread T0 here:
    #0 0x55d87f98e0a1 in operator new[](unsigned long) (/src/test/results/fuzzing_openimageio/test/OpenImageIO/dist/bin/iinfo+0x11c0a1) (BuildId: b43058b69e4695b86490b4cc450eda2919868e7c)
    #1 0x7fc024a3ca8c in OpenImageIO::v3_1::ImageInput::read_tile(int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:644:33
    #2 0x7fc024de23bb in OpenImageIO::v3_2_0::TIFFInput::read_tile(int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2790:27
    #3 0x7fc024a3eacc in OpenImageIO::v3_1::ImageInput::read_tiles(int, int, int, int, int, int, int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:843:31
    #4 0x7fc024de26b4 in OpenImageIO::v3_2_0::TIFFInput::read_tiles(int, int, int, int, int, int, int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:2821:27
    #5 0x7fc024a42319 in OpenImageIO::v3_1::ImageInput::read_image(int, int, int, int, OpenImageIO::v3_1::TypeDesc, void*, long, long, long, bool (*)(void*, float), void*) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imageinput.cpp:1150:23
    #6 0x7fc024923b16 in OpenImageIO::v3_1::ImageBufImpl::read(int, int, int, int, bool, OpenImageIO::v3_1::TypeDesc, bool (*)(void*, float), void*, OpenImageIO::v3_2_0::DoLock) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imagebuf.cpp:1465:27
    #7 0x7fc02492b5ca in OpenImageIO::v3_1::ImageBuf::read(int, int, bool, OpenImageIO::v3_1::TypeDesc, bool (*)(void*, float), void*) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/libOpenImageIO/imagebuf.cpp:1528:20
    #8 0x55d87f997512 in read_input(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, OpenImageIO::v3_1::ImageBuf&, int, int) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:64:13
    #9 0x55d87f997512 in print_stats(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, OpenImageIO::v3_1::ImageSpec const&, int, int, bool) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:81:10
    #10 0x55d87f997512 in print_info_subimage(int, int, OpenImageIO::v3_1::ImageSpec&, OpenImageIO::v3_1::ImageInput*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:316:18
    #11 0x55d87f997512 in print_info(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, unsigned long, OpenImageIO::v3_1::ImageInput*, OpenImageIO::v3_1::ImageSpec&, bool, bool, long long&) /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:421:14
    #12 0x55d87f990c32 in main /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/iinfo/iinfo.cpp:492:14
    #13 0x7fc023a7f1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #14 0x7fc023a7f28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #15 0x55d87f8acad4 in _start (/src/test/results/fuzzing_openimageio/test/OpenImageIO/dist/bin/iinfo+0x3aad4) (BuildId: b43058b69e4695b86490b4cc450eda2919868e7c)

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/test/results/fuzzing_openimageio/test/OpenImageIO/src/tiff.imageio/tiffinput.cpp:1852:38 in OpenImageIO::v3_2_0::TIFFInput::bit_convert(int, unsigned char const*, int, void*, int)
Shadow bytes around the buggy address:
  0x527000002e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x527000002f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x527000002f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x527000003000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x527000003080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x527000003100:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x527000003180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x527000003200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x527000003280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x527000003300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x527000003380: 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
==144981==ABORTING

PoC
poc.tif

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions