GHSA-p42q-g5c9-mh9w
CVE Information
Summary
OpenEXR v3.4.13 has an ILP32-only input-side out-of-bounds read in deep-tiled sample-count decoding. A malformed
deeptilefile can combine a huge valid tile area with a 4-byte sample-count table so that OpenEXRCore computes the sample-count workspace size in 32-bitsize_tand wraps. The public C decode path crashes an unsanitized 32-bit binary with SIGSEGV, ASAN confirms a READ crash inOpenEXRCore/decoding.c, and the public C++DeepTiledInputFile::readPixelSampleCounts()path produces an ASAN heap-buffer-overflow READ.Details
The malformed file is a
deeptileimage withNO_COMPRESSION, oneFLOATchannel, data/tile size268435456 x 4, and a deep chunk whosesample_count_table_sizeis only 4 bytes.Relevant source:
sources/openexr-3.4.13/src/lib/OpenEXRCore/validation.c:593-595: each tile dimension is rejected only when it exceedsINT_MAX / 4;268435456 x 4passes.sources/openexr-3.4.13/src/lib/OpenEXRCore/chunk.c:1368-1371: the deep-tile sample-count table is checked only for non-negative/multiple-of-4 shape underNO_COMPRESSION, not againsttile_width * tile_height * sizeof(int32_t).sources/openexr-3.4.13/src/lib/OpenEXRCore/chunk.c:1415-1418: the attacker-controlled table size is stored incinfo->sample_count_table_size.sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:29-36:sampsizeis computed as(size_t)width * (size_t)height [ + 1 ] * 4. On ILP32,268435456 * 4 * 4wraps to zero, and(268435456 * 4 + 1) * 4wraps to four.sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:227-267:unpack_sample_table()iterates over the full logicalw*hsample table.sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:690and:968-1063:DeepTiledInputFile::readPixelSampleCounts()reachesexr_decoding_run()withEXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL.Two decode modes show the issue:
- Core C API sample-data-only decode without
EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL:sampsizewraps to 0,sample_count_tableis null/zero-sized, and the original ILP32 binary crashes with SIGSEGV. ASAN reports a READ crash atdecoding.c:256inunpack_sample_table().- C++
DeepTiledInputFile::readPixelSampleCounts(): the individual-count flag makes wrappedsampsize == 4, aliases the 4-byte packed sample-count table, and ASAN reports a heap-buffer-overflow READ atdecoding.c:236throughDeepTiledInputFile::readPixelSampleCounts().PoC
Package contents
poc.zipcontains only the files needed to rebuild, run, and verify the standalone malformed EXR PoC.reproduce.sh repro/build_repro.sh repro/run_repro.sh poc_artifacts/poc_ilp32_deeptiled_input_sampletable.exr poc_artifacts/repro_ilp32_deeptiled_input_sampletable_file.cpp poc_artifacts/repro_ilp32_deeptiled_input_sampletable_cpp.cpp logs/original_core.stdout logs/original_core.stderr logs/original_core.rc logs/asan_core.stdout logs/asan_core.stderr logs/asan_core.rc logs/ubsan_core.stdout logs/ubsan_core.stderr logs/ubsan_core.rc logs/asan_cpp.stdout logs/asan_cpp.stderr logs/asan_cpp.rcReproduction
Run from the OpenEXR v3.4.13 repository root after the existing ILP32 original/ASAN/UBSAN builds are present:
mkdir -p artifacts/tmp/report20-poc unzip -o 'report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc.zip' -d artifacts/tmp/report20-poc artifacts/tmp/report20-poc/repro/build_repro.sh original artifacts/tmp/report20-poc/repro/build_repro.sh asan artifacts/tmp/report20-poc/repro/build_repro.sh ubsan artifacts/tmp/report20-poc/repro/run_repro.sh original core artifacts/tmp/report20-poc/repro/run_repro.sh asan core artifacts/tmp/report20-poc/repro/run_repro.sh ubsan core artifacts/tmp/report20-poc/repro/run_repro.sh asan cppA top-level helper runs the same sequence:
./reproduce.shExpected results:
original core -> rc=139, SIGSEGV while decoding the malformed deep-tiled EXR ASAN core -> rc=134, READ crash at decoding.c:256 in unpack_sample_table() UBSAN core -> rc=134, null pointer passed to memcpy at decoding.c:630 ASAN C++ -> rc=134, heap-buffer-overflow READ via DeepTiledInputFile::readPixelSampleCounts()Full original core-path output
logs/original_core.rc:139
logs/original_core.stderr:timeout: the monitored command dumped core
logs/original_core.stdoutis empty:Full ASAN core-path output
logs/asan_core.rc:134
logs/asan_core.stderr:AddressSanitizer:DEADLYSIGNAL ================================================================= ==3705280==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000 (pc 0x567af6c0 bp 0xffae3778 sp 0xffae3720 T0) ==3705280==The signal is caused by a READ memory access. ==3705280==Hint: address points to the zero page. #0 0x567af6c0 in unpack_sample_table /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:256:59 #1 0x567aea67 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:644:14 #2 0x5672babf in main /home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc_artifacts/repro_ilp32_deeptiled_input_sampletable_file.cpp:20:21 #3 0xf7914cb8 (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #4 0xf7914d7b in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x24d7b) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #5 0x5664f2c6 in _start (/home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/repro/bin/repro_ilp32_deeptiled_input_sampletable_file_asan+0x252c6) (BuildId: 10ea841b5f1ec604d2b13be1a2620b4e0e9a8e4c) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:256:59 in unpack_sample_table ==3705280==ABORTING timeout: the monitored command dumped core
logs/asan_core.stdoutis empty:Full UBSAN core-path output
logs/ubsan_core.rc:134
logs/ubsan_core.stderr:/home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:630:25: runtime error: null pointer passed as argument 1, which is declared to never be null /usr/include/string.h:44:28: note: nonnull attribute specified here #0 0x566bc006 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:629:21 #1 0x5666edfb in main /home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc_artifacts/repro_ilp32_deeptiled_input_sampletable_file.cpp:20:21 #2 0xf7963cb8 (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #3 0xf7963d7b in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x24d7b) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #4 0x5663ee66 in _start (/home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/repro/bin/repro_ilp32_deeptiled_input_sampletable_file_ubsan+0x3ce66) (BuildId: 85e97162032e138ff7e9ea276a516d523ddfce40) SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:630:25 timeout: the monitored command dumped core
logs/ubsan_core.stdoutis empty:Full ASAN C++ DeepTiledInputFile output
logs/asan_cpp.rc:134
logs/asan_cpp.stderr:reading /home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc_artifacts/poc_ilp32_deeptiled_input_sampletable.exr via DeepTiledInputFile::readPixelSampleCounts() ================================================================= ==3705309==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf5a00634 at pc 0x56964939 bp 0xffd93c78 sp 0xffd93c70 READ of size 4 at 0xf5a00634 thread T0 #0 0x56964938 in unpack_sample_table /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:236:59 #1 0x56963857 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:644:14 #2 0x567c9ef6 in Imf_3_4::(anonymous namespace)::TileProcess::run_decode(_priv_exr_context_t const*, int, Imf_3_4::DeepFrameBuffer const*, std::vector<Imf_3_4::DeepSlice, std::allocator<Imf_3_4::DeepSlice>> const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:1063:23 #3 0x567c20cc in Imf_3_4::DeepTiledInputFile::Data::readTiles(int, int, int, int, int, int, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:968:20 #4 0x567c6c8c in Imf_3_4::DeepTiledInputFile::readPixelSampleCounts(int, int, int, int, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:690:16 #5 0x567ba85c in main /home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc_artifacts/repro_ilp32_deeptiled_input_sampletable_cpp.cpp:35:14 #6 0xf7954cb8 (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #7 0xf7954d7b in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x24d7b) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) #8 0x566de1a6 in _start (/home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/repro/bin/repro_ilp32_deeptiled_input_sampletable_cpp_asan+0x991a6) (BuildId: c12e906fe46d4329455538140a8f858ec1c7346b) 0xf5a00634 is located 0 bytes after 4-byte region [0xf5a00630,0xf5a00634) allocated by thread T0 here: #0 0x567759d7 in malloc (/home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/repro/bin/repro_ilp32_deeptiled_input_sampletable_cpp_asan+0x1309d7) (BuildId: c12e906fe46d4329455538140a8f858ec1c7346b) #1 0x569c878c in internal_exr_alloc /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/memory.c:39:12 #2 0x569ca44e in internal_decode_alloc_buffer /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/coding.c:256:22 #3 0x56962e91 in default_read_chunk /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:161:14 #4 0x569636d7 in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:567:10 #5 0x567c9ef6 in Imf_3_4::(anonymous namespace)::TileProcess::run_decode(_priv_exr_context_t const*, int, Imf_3_4::DeepFrameBuffer const*, std::vector<Imf_3_4::DeepSlice, std::allocator<Imf_3_4::DeepSlice>> const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:1063:23 #6 0x567c20cc in Imf_3_4::DeepTiledInputFile::Data::readTiles(int, int, int, int, int, int, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:968:20 #7 0x567c6c8c in Imf_3_4::DeepTiledInputFile::readPixelSampleCounts(int, int, int, int, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfDeepTiledInputFile.cpp:690:16 #8 0x567ba85c in main /home/br0nzu/openexr/v3.4.13/report/20. OpenEXR v3.4.13 ILP32 DeepTiledInputFile sample count table decode OOB read/poc_artifacts/repro_ilp32_deeptiled_input_sampletable_cpp.cpp:35:14 #9 0xf7954cb8 (/lib/i386-linux-gnu/libc.so.6+0x24cb8) (BuildId: 0f6a23454651f08cb9b65ff6544d658177b59947) SUMMARY: AddressSanitizer: heap-buffer-overflow /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:236:59 in unpack_sample_table Shadow bytes around the buggy address: 0xf5a00380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0xf5a00400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0xf5a00480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0xf5a00500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0xf5a00580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0xf5a00600: fa fa fa fa fa fa[04]fa fa fa 00 fa fa fa 00 fa 0xf5a00680: fa fa 00 fa fa fa 00 fa fa fa 00 04 fa fa 00 fa 0xf5a00700: fa fa 00 00 fa fa 02 fa fa fa fd fd fa fa 00 00 0xf5a00780: fa fa 04 fa fa fa 00 00 fa fa fa fa fa fa fa fa 0xf5a00800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0xf5a00880: 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 ==3705309==ABORTING timeout: the monitored command dumped core
logs/asan_cpp.stdoutis empty:Impact
Applications that build OpenEXR v3.4.13 for 32-bit ILP32 targets and decode attacker-controlled deep-tiled EXR files can be crashed by a small malformed file. The demonstrated impact is denial of service via SIGSEGV in an unsanitized original binary, plus sanitizer-confirmed out-of-bounds read/null-pointer access in the OpenEXRCore deep sample-count decode path. Arbitrary code execution and out-of-bounds write were not proven. LP64 builds are not demonstrated because the vulnerable arithmetic does not truncate to 32-bit
size_tthere.