Skip to content

GHSA-7hgm-jxjc-6hgg on CTRL-OS 26.05

Aliases: GHSA-7hgm-jxjc-6hgg

Packages: openexr

Status: Plausible

Advisory Information

OpenEXR v3.4.13 NO_COMPRESSION scanline short chunk uninitialized pixel disclosure

Summary

A crafted scanline EXR using NO_COMPRESSION can declare a chunk packed_size smaller than the computed unpacked_size. In the generic OpenEXRCore decode path, OpenEXR allocates a full-size unpack buffer but does not populate or zero-fill it for this short uncompressed chunk. The later unpack routine then copies bytes from that uninitialized internal buffer into caller-visible pixel output.

This is a file-triggered information disclosure issue. It does not require an invalid caller buffer.

Reproduction status

  • v3.4.13 exrcheck original/ASAN/UBSAN accepts the crafted file as OK.
  • v3.4.13 original/ASAN/UBSAN/MSAN public API harnesses accept the safe control and detect allocator-pattern disclosure on the PoC.
  • A local main build in this workspace shows the same harness result.
  • Freshly fetched origin/main commit 6820342 (Fix Name::operator= to null-terminate long strings (#2500), 2026-06-23) also reproduces in a fresh Release build: exrcheck returns 0, safe control returns 0, PoC harness returns 134 after detecting disclosure.

Technical details

The expected invariant is:

For scanline chunks with compression = NO_COMPRESSION, packed_size should equal the computed unpacked_size, or the decoder must reject/zero-fill before generic unpack reads the full unpacked region.

In v3.4.13, the scanline chunk-info path accepts a short nonzero packed size as long as it is not larger than the per-chunk unpacked size:

  • src/lib/OpenEXRCore/chunk.c computes scanline unpacked_size.
  • The scanline path rejects packed_size == 0, but does not reject 0 < packed_size < unpacked_size for NO_COMPRESSION.
  • The tile path has a stricter NO_COMPRESSION packed/unpacked equality check, but the scanline path lacks the equivalent check.

The generic decode path is then reachable with a normal non-direct output layout, such as padded pixel stride:

  • src/lib/OpenEXRCore/decoding.c allocates decode->unpacked_buffer when packed_size != unpacked_size.
  • For EXR_COMPRESSION_NONE, no decompressor copies short packed data into that full buffer.
  • exr_decoding_run() still invokes the selected unpack routine because chunk.unpacked_size > 0.
  • src/lib/OpenEXRCore/unpack.c reads from decode->unpacked_buffer and writes those bytes to the caller's output channel.

The PoC file is a 64x1 single-channel FLOAT scanline EXR with NO_COMPRESSION. The safe control has packed_size = unpacked_size = 256. The PoC changes the first chunk leader's packed_size to 16 while keeping the file otherwise structurally valid enough for exrcheck to report OK.

PoC files

poc.zip contains only the files needed to reproduce and inspect the issue:

poc.zip

  • reproduce.sh — replay script for the local v3.4.13/main harness builds.
  • poc_materials/base_none_64x1_float.exr — safe control.
  • poc_materials/poc_none_short_16_of_256.exr — crafted short uncompressed scanline chunk.
  • poc_materials/make_none_base.cpp — safe-control generator.
  • poc_materials/patch_none_short.py — packed-size patcher.
  • poc_materials/fuzz_none_short_uncompressed_api.cpp — focused public OpenEXRCore API harness.
  • poc_materials/logs/final/ — full current reproduction logs.

Run from this report directory:

./reproduce.sh

Current reproduction output

Full stdout from the current replay:

===== exrcheck_original_poc =====
rc=0
 file /home/br0nzu/openexr/v3.4.13/report/39. OpenEXR v3.4.13 NO_COMPRESSION scanline short chunk uninitialized pixel disclosure/poc_materials/poc_none_short_16_of_256.exr OK
===== exrcheck_asan_poc =====
rc=0
 file /home/br0nzu/openexr/v3.4.13/report/39. OpenEXR v3.4.13 NO_COMPRESSION scanline short chunk uninitialized pixel disclosure/poc_materials/poc_none_short_16_of_256.exr OK
===== exrcheck_ubsan_poc =====
rc=0
 file /home/br0nzu/openexr/v3.4.13/report/39. OpenEXR v3.4.13 NO_COMPRESSION scanline short chunk uninitialized pixel disclosure/poc_materials/poc_none_short_16_of_256.exr OK
===== v3413_original_base =====
rc=0
===== v3413_original_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== v3413_asan_base =====
rc=0
===== v3413_asan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== v3413_ubsan_base =====
rc=0
===== v3413_ubsan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== v3413_msan_base =====
rc=0
===== v3413_msan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== local_main_original_base =====
rc=0
===== local_main_original_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== local_main_asan_base =====
rc=0
===== local_main_asan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== local_main_ubsan_base =====
rc=0
===== local_main_ubsan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)
===== local_main_msan_base =====
rc=0
===== local_main_msan_poc =====
rc=134
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)

Latest fetched origin/main fresh Release build:

origin/main commit: 6820342 2026-06-23 15:24:29 -0700 Fix Name::operator= to null-terminate long strings (#2500)
exrcheck poc rc=0
harness base rc=0
harness poc rc=134
 file /home/br0nzu/openexr/v3.4.13/report/39. OpenEXR v3.4.13 NO_COMPRESSION scanline short chunk uninitialized pixel disclosure/poc_materials/poc_none_short_16_of_256.exr OK
NO_COMPRESSION short chunk leaked allocator pattern into channel 0: 256/256 written bytes (528 payload bytes)

Impact

An attacker who can supply an EXR image to an application that decodes pixels through OpenEXRCore into a non-direct but valid output layout can cause caller-visible decoded pixels to contain stale internal allocation bytes rather than file-provided samples. Applications that display, save, transform, or transmit decoded pixels may expose those bytes.

This report demonstrates information disclosure through decoded pixel output. The harness abort is intentional after detecting the disclosure.

Suggested fix

For scanline chunks, mirror the tile-path invariant for EXR_COMPRESSION_NONE: reject chunks where packed_size != unpacked_size, or explicitly initialize/copy/zero-fill the full unpack buffer before any generic unpack routine can read from 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