Skip to content

GHSA-fwhm-jj7c-mx7v on CTRL-OS 26.05

Aliases: GHSA-fwhm-jj7c-mx7v

Packages: openexr

Status: Plausible

Advisory Information

OpenEXR v3.4.13 deep NO_COMPRESSION scanline short sample data disclosure

Summary

A crafted deep scanline EXR using NO_COMPRESSION can declare a deep sample-data packed_size smaller than unpacked_size. OpenEXRCore accepts the chunk, allocates the full unpack buffer, but no decompressor populates it for NO_COMPRESSION. The deep sample unpack path then copies stale internal allocation bytes into caller-visible deep sample output.

This is file-triggered through public OpenEXRCore decode APIs with valid caller-provided deep sample buffers.

Reproduction status

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=134

PoC abort message in each failing run:

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

The abort is intentional in the harness after detecting allocator-pattern bytes in decoded deep sample output.

Root cause

Deep scanline chunk-info validates the deep sample-count table size and file bounds, but does not enforce the NO_COMPRESSION invariant that packed sample data size must equal unpacked sample data size.

Relevant flow:

src/lib/OpenEXRCore/chunk.c
  exr_read_scanline_chunk_info()
  deep scanline: reads sample_count_table_size, packed_size, unpacked_size
  missing: reject EXR_COMPRESSION_NONE when packed_size != unpacked_size

src/lib/OpenEXRCore/decoding.c
  packed_size != unpacked_size -> allocate full unpacked_buffer
  EXR_COMPRESSION_NONE -> no decompressor fills that buffer
  deep unpack routine copies from unpacked_buffer into caller output

The tiled path already has the missing guard for NO_COMPRESSION; the deep scanline path does not.

PoC package

poc.zip contains only required reproduction material:

poc.zip

reproduce.sh
poc_materials/base_deep_none_4x1.exr
poc_materials/poc_deep_none_short_4_of_16.exr
poc_materials/fuzz_deep_none_short_uncompressed_api.cpp
poc_materials/make_deep_scanline_seed.cpp
poc_materials/patch_deep_none_short.py
poc_materials/logs/final/summary.txt
poc_materials/logs/final/reproduce_run.log
poc_materials/logs/final/*_poc.err

Reproduction

From the extracted poc.zip directory:

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

Expected result: safe control files exit 0; the PoC exits 134 after the harness detects copied stale bytes.

Complete validation output

The sanitizer builds do not emit an ASAN/UBSAN/MSAN stack trace here. The harness detects stale allocator-pattern bytes in caller-visible deep sample output and intentionally aborts, so the complete stderr for each sanitizer PoC run is the single line shown below.

reproduce.sh stdout

===== v3413_original_base =====
rc=0
===== v3413_original_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== v3413_asan_base =====
rc=0
===== v3413_asan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== v3413_ubsan_base =====
rc=0
===== v3413_ubsan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== v3413_msan_base =====
rc=0
===== v3413_msan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== local_main_original_base =====
rc=0
===== local_main_original_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== local_main_asan_base =====
rc=0
===== local_main_asan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== local_main_ubsan_base =====
rc=0
===== local_main_ubsan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== local_main_msan_base =====
rc=0
===== local_main_msan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== origin_main_682_release_base =====
rc=0
===== origin_main_682_release_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
===== origin_main_682_asan_base =====
rc=0
===== origin_main_682_asan_poc =====
rc=134
DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes
PASS

v3.4.13 ASAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

v3.4.13 UBSAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

v3.4.13 MSAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

local main ASAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

local main UBSAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

local main MSAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

fresh origin/main 68203429 ASAN PoC stderr

DEEP NO_COMPRESSION short sample data leaked allocator pattern: 16/16 output bytes

Impact

An attacker-controlled EXR file can cause decoded deep sample data to contain stale process memory bytes instead of file-provided sample data. Applications that decode and then display, convert, save, or transmit deep samples may expose those bytes.

This report demonstrates information disclosure.

Suggested fix

For deep scanline chunks, mirror the tiled-path check: reject EXR_COMPRESSION_NONE chunks where packed_size != unpacked_size, or otherwise ensure the full unpack buffer is initialized before any deep sample unpack routine reads it.

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