GHSA-38j8-88v6-jrm7
CVE Information
OpenEXR v3.4.13 DWA UNKNOWN short inflate uninitialized pixel disclosure
Summary
A crafted DWAA/DWAB EXR chunk can declare a DWA
UNKNOWNplanar stream with a large uncompressed size, while the embedded zlib stream inflates to only a short prefix. OpenEXR accepts the short inflate and later copies the full declaredUNKNOWNchannel region to caller-visible pixel output. The unwritten tail of the DWA planar buffer can therefore expose stale heap bytes.This is file-triggered through normal OpenEXRCore decode APIs.
Reproduction status
v3.4.13 exrcheck original/ASAN/UBSAN: PoC accepted as OK v3.4.13 original/ASAN/UBSAN/MSAN: safe control rc=0, PoC rc=134 local main original/ASAN/UBSAN/MSAN: safe control rc=0, PoC rc=134 fresh origin/main 68203429 Release/ASAN: safe control rc=0, PoC rc=134PoC abort message in each failing harness run:
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytesThe abort is intentional in the harness after detecting allocator-pattern bytes in decoded caller-visible pixel output.
Root cause
The DWA
UNKNOWNstream decode path does not verify that the zlib output filled the declaredUNKNOWN_UNCOMPRESSED_SIZE.Relevant flow:
src/lib/OpenEXRCore/internal_dwa_compressor.h DwaCompressor_uncompress() exr_uncompress_buffer(..., compressedUnknownBuf, unknownCompressedSize, me->_planarUncBuffer[UNKNOWN], unknownUncompressedSize, NULL) passes NULL for actual_out, so a short successful inflate is not detected src/lib/OpenEXRCore/internal_dwa_compressor.h case UNKNOWN memcpy(dcddata->_rows[row], cd->planarUncBufferEnd, dstScanlineSize) copies the full declared UNKNOWN channel region to output rowsThe PoC is a 64x32 single HALF channel named
X. That channel is classified as DWAUNKNOWN; the declared UNKNOWN uncompressed size is4096bytes, but the zlib stream inflates to only20bytes. The remaining4076bytes of the planar buffer are stale allocation contents and are copied to the output channel.PoC package
poc.zipcontains only required reproduction material:reproduce.sh poc_materials/base_dwaa_unknown_x.exr poc_materials/poc_dwaa_unknown_short_20_of_4096.exr poc_materials/fuzz_dwa_unknown_short_inflate_api.cpp poc_materials/make_dwa_unknown_base.cpp poc_materials/make_dwa_unknown_short.py poc_materials/logs/final/reproduce_run.log poc_materials/logs/final/exrcheck_*_poc.out poc_materials/logs/final/*_poc.err
poc.zipintentionally does not includereport.md, README files, checksum-only files, AFL logs, duplicate-gate logs, or source-snippet notes.Reproduction
From the extracted
poc.zipdirectory:OPENEXR_ROOT=/home/br0nzu/openexr/v3.4.13 ./reproduce.shExpected result: safe controls exit
0; the crafted PoC exits134after the harness detects stale bytes in caller-visible output.Complete validation output
The sanitizer builds do not emit an ASAN/UBSAN/MSAN stack trace here. The harness detects stale allocator-pattern bytes and intentionally aborts, so the complete stderr for each sanitizer PoC run is the single line shown below.
reproduce.sh stdout
===== exrcheck_original_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/36. OpenEXR v3.4.13 DWA UNKNOWN short inflate uninitialized pixel disclosure/poc_materials/poc_dwaa_unknown_short_20_of_4096.exr OK ===== exrcheck_asan_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/36. OpenEXR v3.4.13 DWA UNKNOWN short inflate uninitialized pixel disclosure/poc_materials/poc_dwaa_unknown_short_20_of_4096.exr OK ===== exrcheck_ubsan_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/36. OpenEXR v3.4.13 DWA UNKNOWN short inflate uninitialized pixel disclosure/poc_materials/poc_dwaa_unknown_short_20_of_4096.exr OK ===== v3413_original_base ===== rc=0 ===== v3413_original_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_asan_base ===== rc=0 ===== v3413_asan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_ubsan_base ===== rc=0 ===== v3413_ubsan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_msan_base ===== rc=0 ===== v3413_msan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_original_base ===== rc=0 ===== main_original_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_asan_base ===== rc=0 ===== main_asan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_ubsan_base ===== rc=0 ===== main_ubsan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_msan_base ===== rc=0 ===== main_msan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== origin_main_682_release_base ===== rc=0 ===== origin_main_682_release_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes ===== origin_main_682_asan_base ===== rc=0 ===== origin_main_682_asan_poc ===== rc=134 DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytes PASSv3.4.13 ASAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytesv3.4.13 UBSAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytesv3.4.13 MSAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 byteslocal main ASAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 byteslocal main UBSAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 byteslocal main MSAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytesfresh origin/main 68203429 ASAN PoC stderr
DWA UNKNOWN short inflate leaked allocator pattern into channel 0: 4076/4096 bytesImpact
This demonstrates information disclosure / uninitialized data exposure from decoded pixel output. An attacker-controlled EXR image can cause decoded DWA
UNKNOWNchannel pixels to contain stale bytes from OpenEXR's decode allocation instead of file-provided samples. Applications that display, save, transform, or transmit decoded pixels may expose those bytes.Suggested fix
For DWA
UNKNOWNdata, pass anactual_outpointer toexr_uncompress_buffer()and reject the chunk unlessactual_out == unknownUncompressedSize, or otherwise guarantee the entire UNKNOWN planar buffer is initialized before row copy-out.