GHSA-wwx8-2v36-rhr8 on CTRL-OS 26.05
Aliases: GHSA-wwx8-2v36-rhr8
Packages: openexr
Status: Plausible
Advisory Information
OpenEXR v3.4.13 DWA RLE short raw uninitialized pixel disclosure
Summary
A crafted DWAA/DWAB EXR chunk can provide a valid but too-short DWA
RLEraw stream for an RLE-classified channel. OpenEXR accepts the chunk, expands only the attacker-declaredRLE_RAW_SIZE, and then reconstructs the full channel from_planarUncBuffer[RLE]. The unwritten tail of that planar buffer can be copied into caller-visible pixel output.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 d9de1caa original/ASAN/UBSAN: safe control rc=0, PoC rc=134PoC abort message in each failing harness run:
DWA RLE short raw 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
RLEstream decode path validates that the compressed stream expands to the declaredRLE_RAW_SIZE, but it does not verify thatRLE_RAW_SIZEis large enough for all RLE-classified channels in the chunk.Relevant flow:
src/lib/OpenEXRCore/internal_dwa_compressor.h reads RLE_COMPRESSED_SIZE, RLE_UNCOMPRESSED_SIZE, and RLE_RAW_SIZE rejects RLE_RAW_SIZE only when it exceeds the chunk's total uncompressed size zlib + internal RLE expansion can succeed for the short attacker-declared RLE_RAW_SIZE src/lib/OpenEXRCore/internal_dwa_compressor.h RLE channel reconstruction calls interleaveByte2()/copy logic for the full channel width/height no initialized-end check is applied against the bytes actually produced into _planarUncBuffer[RLE]The PoC is a 64x32 single HALF channel named
A, which is classified as DWARLE. The channel requires4096bytes, but the DWA RLE counters are patched so the internal RLE stream expands to only20raw bytes. The remaining4076bytes of the planar buffer are stale allocation contents and are copied to the output channel.PoC DWA counters:
off_table 277 chunk_off 285 y 0 packed_size 104 payload_len 104 VERSION 2 UNKNOWN_UNCOMPRESSED_SIZE 0 UNKNOWN_COMPRESSED_SIZE 0 AC_COMPRESSED_SIZE 0 DC_COMPRESSED_SIZE 0 RLE_COMPRESSED_SIZE 10 RLE_UNCOMPRESSED_SIZE 2 RLE_RAW_SIZE 20 AC_UNCOMPRESSED_COUNT 0 DC_UNCOMPRESSED_COUNT 0 AC_COMPRESSION 0 rule_size16 6 bytes 060041000801 streams start 94 remaining 10 UNKNOWN size 0 off 94 head AC size 0 off 94 head DC size 0 off 94 head RLE size 10 off 94 head 78da1366000000280014 zlib dec len 2 head 1300PoC package
poc.zipcontains only required reproduction material:reproduce.sh poc_materials/base_dwaa_rle_a.exr poc_materials/poc_dwaa_rle_short_20_of_4096.exr poc_materials/fuzz_dwa_rle_short_raw_api.cpp poc_materials/make_dwa_rle_base.cpp poc_materials/make_dwa_rle_short.py poc_materials/parse_dwa.py poc_materials/logs/final/reproduce_run.log poc_materials/logs/final/poc_parse.txt poc_materials/logs/final/base_parse.txt 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 in the full output below.
reproduce.sh stdout
===== exrcheck_original_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/44. OpenEXR v3.4.13 DWA RLE short raw uninitialized pixel disclosure/poc_materials/poc_dwaa_rle_short_20_of_4096.exr OK ===== exrcheck_asan_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/44. OpenEXR v3.4.13 DWA RLE short raw uninitialized pixel disclosure/poc_materials/poc_dwaa_rle_short_20_of_4096.exr OK ===== exrcheck_ubsan_poc ===== rc=0 file /home/br0nzu/openexr/v3.4.13/report/44. OpenEXR v3.4.13 DWA RLE short raw uninitialized pixel disclosure/poc_materials/poc_dwaa_rle_short_20_of_4096.exr OK ===== v3413_original_base ===== rc=0 ===== v3413_original_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_asan_base ===== rc=0 ===== v3413_asan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_ubsan_base ===== rc=0 ===== v3413_ubsan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== v3413_msan_base ===== rc=0 ===== v3413_msan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_original_base ===== rc=0 ===== main_original_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_asan_base ===== rc=0 ===== main_asan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_ubsan_base ===== rc=0 ===== main_ubsan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== main_msan_base ===== rc=0 ===== main_msan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== origin_main_original_base ===== rc=0 ===== origin_main_original_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== origin_main_asan_base ===== rc=0 ===== origin_main_asan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes ===== origin_main_ubsan_base ===== rc=0 ===== origin_main_ubsan_poc ===== rc=134 DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes PASSImpact
This demonstrates information disclosure / uninitialized data exposure from decoded pixel output. An attacker-controlled EXR image can cause decoded DWA
RLEchannel 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.The demonstrated impact is caller-visible stale pixel data.
Suggested fix
For DWA
RLEdata, reject the chunk unless the bytes produced into the RLE planar buffer are sufficient for every RLE-classified channel that will be reconstructed, or otherwise guarantee the full RLE planar region is initialized before interleave/copy to caller-visible pixels.
Updates
2026-08-11 03:50 CEST
Metadata changes:
- Status for package
openexr: “Plausible”
2026-08-11 03:32 CEST
Metadata changes:
- Status for package
openexr: “New”