Skip to content

GHSA-pqp9-558c-453q on CTRL-OS 26.05

Aliases: GHSA-pqp9-558c-453q

Packages: openexr

Status: Plausible

Advisory Information

Summary

A crafted EXR with a non-zero dataWindow.min can make OpenEXRUtil's documented TypedFlatImageChannel<T>::row() API return an invalid heap pointer. When an application loads the file and writes rows through FlatHalfChannel::row(), the original v3.4.12 build crashes with SIGSEGV, and the ASAN build reports a write to invalid/freed heap memory.

Details

Root cause:

  • src/lib/OpenEXRUtil/ImfFlatImageChannel.h documents row(int r) as 0-based row access.
  • src/lib/OpenEXRUtil/ImfFlatImageChannel.h:200-203 returns _base + r * pixelsPerRow().
  • src/lib/OpenEXRUtil/ImfFlatImageChannel.cpp:131-135 adjusts _base by dataWindow.min for absolute-coordinate pixel access.

These models conflict. operator()(x,y) / at(x,y) use EXR pixel coordinates, but row(r) is documented as 0-based. For a non-zero dataWindow.min, row(0) points outside the allocated _pixels storage.

PoC input:

  • file: poc_zip_nonzero_dw.exr
  • dataWindow: (20, 20) - (197, 174)
  • channels: B, G, R, all HALF

The attached poc.zip includes the EXR input, a minimal row-write reproducer, and build/run scripts. Local verification with those files produced:

  • original/plain: SIGSEGV, rc=139
  • original/resize: SIGSEGV, rc=139
  • ASAN/plain: SEGV caused by WRITE in the row() write
  • ASAN/resize: heap-use-after-free, WRITE of size 2

PoC

Reproduction

poc.zip contains:

poc.zip

README.md
poc_zip_nonzero_dw.exr
repro/repro_row_spray.cpp
repro/build_repro.sh
repro/run_repro.sh

Run from the OpenEXR v3.4.12 repository root:

mkdir -p artifacts/tmp/report17-poc
unzip -o 'report/17. OpenEXRUtil FlatImageChannel row nonzero dataWindow heap OOB write/poc.zip' -d artifacts/tmp/report17-poc

artifacts/tmp/report17-poc/repro/build_repro.sh original
artifacts/tmp/report17-poc/repro/build_repro.sh asan

artifacts/tmp/report17-poc/repro/run_repro.sh original both
artifacts/tmp/report17-poc/repro/run_repro.sh asan both

Expected results:

original/plain  -> SIGSEGV, rc=139
original/resize -> SIGSEGV, rc=139
ASAN/plain      -> SEGV caused by WRITE in row() write
ASAN/resize     -> heap-use-after-free, WRITE of size 2

ASAN log excerpt

Representative ASAN output from the included reproducer with resize:

ERROR: AddressSanitizer: heap-use-after-free on address 0x52f000028808
WRITE of size 2 at 0x52f000028808 thread T0
    #0 ... main ... repro_row_spray.cpp:30:34
freed by thread T0 here:
    #0 ... operator delete[](void*)
    #1 ... TypedFlatImageChannel<half>::~TypedFlatImageChannel() ... ImfFlatImageChannel.cpp:75:5
    #2 ... FlatImageLevel::clearChannels() ... ImfFlatImageLevel.cpp:125:9
    #3 ... Image::resize(...) ... ImfImage.cpp:298:9
previously allocated by thread T0 here:
    #0 ... operator new[](unsigned long)
    #1 ... TypedFlatImageChannel<half>::resize() ... ImfFlatImageChannel.cpp:121:15
    #2 ... loadImage(...) ... ImfImageIO.cpp:117:17
SUMMARY: AddressSanitizer: heap-use-after-free ... repro_row_spray.cpp:30:34 in main

Impact

This is heap memory corruption reachable from an untrusted EXR file when downstream code uses OpenEXRUtil's documented row API for fast pixel writes. The confirmed effects for the attached PoC are original-binary SIGSEGV and ASAN-confirmed invalid/freed-heap writes. Affected consumers are tools, converters, render pipeline components, or image-processing services that accept untrusted EXR files and use FlatHalfChannel::row() on loaded images.

Updates

2026-08-11 03:51 CEST

Metadata changes:

  • Status for package openexr: “Plausible

2026-08-11 03:30 CEST

Metadata changes:

  • Status for package openexr: “New