Skip to content

GHSA-pgc2-hppj-q623

CVE Information

poc.zip

Summary

A malformed tiled EXR with a positive 65537 x 65537 dataWindow can make the shipped exrmetrics tool corrupt heap memory on ILP32 builds. In initTiled(), numPixels * samplesize is computed as a large 64-bit value but is passed to std::vector<char>::resize(size_type), which truncates on 32-bit builds before tiled decode writes into it. Fresh validation also reproduced the same behavior on current main.

Details

For RGBA HALF tiles, each channel needs 65537 * 65537 * 2 bytes. On ILP32 the vector allocation truncates to 262,146 bytes per channel, while TiledInputPart::readTiles() decodes attacker-controlled tile bytes into that undersized channel buffer.

This PoC uses only a crafted EXR file through the shipped command-line tool.

PoC

From /home/br0nzu/openexr/v3.4.13:

./report/69.\ OpenEXR\ v3.4.13\ exrmetrics\ ILP32\ tiled\ channel\ vector\ heap\ buffer\ overflow/reproduce.sh

PoC input: poc_materials/exrmetrics_tiled_w65537_h65537_tw65537_th4.exr

Validation evidence

v3.4.13: original rc=139, ASAN rc=134, UBSAN rc=139. current-main: original rc=139, ASAN rc=134, UBSAN rc=139.

Current-main and patch-overlap logs are saved under logs/fresh_current_main_ilp32_* and validation_summary.txt in this directory.

Original crash stderr:

timeout: the monitored command dumped core

Full ASAN output

=================================================================
==2540514==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf75ee802 at pc 0x5664678a bp 0xffd85148 sp 0xffd84d20
WRITE of size 131074 at 0xf75ee802 thread T0
    #0 0x56646789 in pread64 (/home/br0nzu/openexr/v3.4.13/report/69. OpenEXR v3.4.13 exrmetrics ILP32 tiled channel vector heap buffer overflow/repro_build/bin/exrmetrics_ilp32_asan+0xcc789) (BuildId: 676a5c8917ae2e87249d3288cbe81298456a53a1)
    #1 0x568a56c1 in default_read_func /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/internal_posix_file_impl.h:177:14
    #2 0x568a0b46 in dispatch_read /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/context.c:51:16
    #3 0x5692676b in read_uncompressed_direct /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:131:18
    #4 0x569274a7 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:567:10
    #5 0x567bf1e5 in Imf_3_4::(anonymous namespace)::TileProcess::run_decode(_priv_exr_context_t const*, int, Imf_3_4::FrameBuffer const*, std::vector<Imf_3_4::Slice, std::allocator<Imf_3_4::Slice>> const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfTiledInputFile.cpp:913:28
    #6 0x567b7ecc in Imf_3_4::TiledInputFile::Data::readTiles(int, int, int, int, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfTiledInputFile.cpp:825:20
    #7 0x567b73e0 in Imf_3_4::TiledInputFile::readTiles(int, int, int, int, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfTiledInputFile.cpp:313:16
    #8 0x567c30df in Imf_3_4::TiledInputPart::readTiles(int, int, int, int, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfTiledInputPart.cpp:166:11
    #9 0x5671d3c3 in readTiled(Imf_3_4::TiledInputPart&, std::vector<Imf_3_4::FrameBuffer, std::allocator<Imf_3_4::FrameBuffer>>&, std::vector<double, std::allocator<double>>&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:253:20
    #10 0x56724457 in initAndReadFile(Imf_3_4::MultiPartInputFile&, std::vector<Imf_3_4::Header, std::allocator<Imf_3_4::Header>> const&, int, std::vector<partData, std::allocator<partData>>&, fileMetrics&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:701:13
    #11 0x567289f2 in exrmetrics(char const*, char const*, int, Imf_3_4::Compression, float, int, bool, bool, PixelMode, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:1090:5
    #12 0x5670bf2d in main /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/main.cpp:540:41
    #13 0xf7971cb8  (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947)
    #14 0xf7971d7b in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x24d7b) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947)
    #15 0x5662a816 in _start (/home/br0nzu/openexr/v3.4.13/report/69. OpenEXR v3.4.13 exrmetrics ILP32 tiled channel vector heap buffer overflow/repro_build/bin/exrmetrics_ilp32_asan+0xb0816) (BuildId: 676a5c8917ae2e87249d3288cbe81298456a53a1)

0xf75ee802 is located 0 bytes after 262146-byte region [0xf75ae800,0xf75ee802)
allocated by thread T0 here:
    #0 0x567045db in operator new(unsigned int) (/home/br0nzu/openexr/v3.4.13/report/69. OpenEXR v3.4.13 exrmetrics ILP32 tiled channel vector heap buffer overflow/repro_build/bin/exrmetrics_ilp32_asan+0x18a5db) (BuildId: 676a5c8917ae2e87249d3288cbe81298456a53a1)
    #1 0x5672ecea in std::__new_allocator<char>::allocate(unsigned int, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27
    #2 0x5672ecea in std::allocator_traits<std::allocator<char>>::allocate(std::allocator<char>&, unsigned int) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20
    #3 0x5672ecea in std::_Vector_base<char, std::allocator<char>>::_M_allocate(unsigned int) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20
    #4 0x5672ecea in std::vector<char, std::allocator<char>>::_M_default_append(unsigned int) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/vector.tcc:663:34
    #5 0x5671c725 in std::vector<char, std::allocator<char>>::resize(unsigned int) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:1016:4
    #6 0x5671c725 in initTiled(std::vector<std::vector<std::vector<char, std::allocator<char>>, std::allocator<std::vector<char, std::allocator<char>>>>, std::allocator<std::vector<std::vector<char, std::allocator<char>>, std::allocator<std::vector<char, std::allocator<char>>>>>>&, std::vector<Imf_3_4::FrameBuffer, std::allocator<Imf_3_4::FrameBuffer>>&, Imf_3_4::TiledInputPart&, Imf_3_4::Header const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:208:58
    #7 0x56723ae1 in initAndReadFile(Imf_3_4::MultiPartInputFile&, std::vector<Imf_3_4::Header, std::allocator<Imf_3_4::Header>> const&, int, std::vector<partData, std::allocator<partData>>&, fileMetrics&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:688:41
    #8 0x567289f2 in exrmetrics(char const*, char const*, int, Imf_3_4::Compression, float, int, bool, bool, PixelMode, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:1090:5
    #9 0x5670bf2d in main /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/main.cpp:540:41
    #10 0xf7971cb8  (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/br0nzu/openexr/v3.4.13/report/69. OpenEXR v3.4.13 exrmetrics ILP32 tiled channel vector heap buffer overflow/repro_build/bin/exrmetrics_ilp32_asan+0xcc789) (BuildId: 676a5c8917ae2e87249d3288cbe81298456a53a1) in pread64
Shadow bytes around the buggy address:
  0xf75ee580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0xf75ee600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0xf75ee680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0xf75ee700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0xf75ee780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0xf75ee800:[02]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0xf75ee880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0xf75ee900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0xf75ee980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0xf75eea00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0xf75eea80: 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
==2540514==ABORTING
timeout: the monitored command dumped core

Full UBSAN output

timeout: the monitored command dumped core

Impact

Impact is ILP32-specific. Workflows that run ILP32 exrmetrics on untrusted tiled EXR files can be crashed and can write decoded tile bytes past a heap allocation.