Skip to content

GHSA-wwx8-2v36-rhr8

CVE 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 RLE raw stream for an RLE-classified channel. OpenEXR accepts the chunk, expands only the attacker-declared RLE_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=134

PoC abort message in each failing harness run:

DWA RLE short raw leaked allocator pattern into channel 0: 4076/4096 bytes

The abort is intentional in the harness after detecting allocator-pattern bytes in decoded caller-visible pixel output.

Root cause

The DWA RLE stream decode path validates that the compressed stream expands to the declared RLE_RAW_SIZE, but it does not verify that RLE_RAW_SIZE is 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 DWA RLE. The channel requires 4096 bytes, but the DWA RLE counters are patched so the internal RLE stream expands to only 20 raw bytes. The remaining 4076 bytes 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 1300

PoC package

poc.zip contains only required reproduction material:

poc.zip

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.zip intentionally does not include report.md, README files, checksum-only files, AFL logs, duplicate-gate logs, or source-snippet notes.

Reproduction

From the extracted poc.zip directory:

OPENEXR_ROOT=/home/br0nzu/openexr/v3.4.13 ./reproduce.sh

Expected result: safe controls exit 0; the crafted PoC exits 134 after 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
PASS

Impact

This demonstrates information disclosure / uninitialized data exposure from decoded pixel output. An attacker-controlled EXR image can cause decoded DWA RLE channel 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 RLE data, 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.