GHSA-fhfq-mv64-6q4j on CTRL-OS 26.05
Aliases: GHSA-fhfq-mv64-6q4j
Packages: openexr
Status: Plausible
Advisory Information
Summary
OpenEXR::InputFile::rawPixelDataToBuffer()/ScanLineInputFile::rawPixelDataToBuffer()can write raw scanline bytes past a caller-provided buffer when the caller passes a negativeint pixelDataSizevalue. The API documents thatpixelDatais pre-allocated forpixelDataSizebytes, but the implementation compares the file chunk size againststatic_cast<uint64_t>(pixelDataSize). A value such as-1becomes a very large unsigned value, bypasses the too-small-buffer check, and the raw EXR chunk is copied into an undersized buffer.This is not triggered by opening an EXR file alone: a consuming application must pass a negative or otherwise unchecked signed size into the public raw-pixel API. With that precondition, a valid scanline EXR controls the copied raw chunk length and bytes.
Confirmed locally on OpenEXR v3.4.12 and v3.4.13: the original build corrupts a canary immediately after a declared 16-byte buffer, and ASAN reports a heap-buffer-overflow
WRITE of size 384atImfScanLineInputFile.cpp:378.Details
The public API contract says the caller supplies an external buffer sized by
pixelDataSize:
src/lib/OpenEXR/ImfInputFile.h:237-254documentsrawPixelDataToBuffer(scanLine, pixelData, pixelDataSize)and sayspixelDatashould be pre-allocated with space forpixelDataSizechars.src/lib/OpenEXR/ImfInputFile.cpp:245-249forwardsInputFile::rawPixelDataToBuffer()to the scanline implementation.src/lib/OpenEXR/ImfInputPart.cpp:101-105exposes the same operation for multipart input.The vulnerable check/copy sequence is in
src/lib/OpenEXR/ImfScanLineInputFile.cpp:357-378in both v3.4.12 and v3.4.13:if (cinfo.packed_size > static_cast<uint64_t> (pixelDataSize)) { THROW (... "Provided buffer is too small" ...); } pixelDataSize = static_cast<int> (cinfo.packed_size); if (EXR_ERR_SUCCESS != exr_read_chunk (_ctxt, _data->partNumber, &cinfo, pixelData)) { THROW (...); }For a positive undersized value, for example
pixelDataSize = 16with a 384-byte raw scanline chunk, OpenEXR throws before copying. ForpixelDataSize = -1, the cast produces a very large unsigned value, so the same 384-byte chunk passes the check. The function then changespixelDataSizeto384and callsexr_read_chunk()with the original small pointer.The PoC uses a valid 64x64 RGB HALF scanline EXR with
NO_COMPRESSION; its first raw scanline chunk is 384 bytes. The reproducer has two relevant modes:
RAWPIXEL_MODE=canary: allocates a larger region but treats only the first 16 bytes as the caller's declared buffer, then checks whether bytes after that logical boundary changed. This confirms the original build writes beyond the declared boundary.RAWPIXEL_MODE=heap: provides an actual 16-byte heap allocation. This makes ASAN report the physical heap-buffer-overflow at the OpenEXR raw chunk read path.I am not claiming target address selection, sensitive-data disclosure, or stronger impact. The demonstrated issue is an out-of-bounds write through the public raw-pixel API when the caller passes a negative size.
PoC
poc.zipcontains:README.md report.md inputs/poc_valid_raw_scanline_rgb.exr repro/repro_rawpixel_negative_size.cpp repro/build_repro.sh repro/run_repro.sh logs/validation_summary.md logs/v3412_original_positive16.{rc,stderr} logs/v3412_original_canary.{rc,stderr} logs/v3412_asan_heap.{rc,stderr} logs/v3413_original_positive16.{rc,stderr} logs/v3413_original_canary.{rc,stderr} logs/v3413_asan_heap.{rc,stderr} source_excerpts/rawPixelDataToBuffer_source.txtReproduce from the OpenEXR workspace root after building the original and ASAN variants:
mkdir -p artifacts/tmp/report22-submit unzip -o 'report/22. OpenEXR rawPixelDataToBuffer negative size heap OOB write/poc.zip' \ -d artifacts/tmp/report22-submit # v3.4.12 build tree: sources/openexr-3.4.12 + builds/{original,asan} artifacts/tmp/report22-submit/repro/run_repro.sh "$PWD" v3412 # Optional v3.4.13 comparison build tree, if present: artifacts/tmp/report22-submit/repro/run_repro.sh "$PWD" v3413Expected core results:
original_positive16_canary rc=0 original_negative_canary rc=77 asan_negative_heap rc=134 core_validation=1Direct commands after building the included reproducer:
POC=artifacts/tmp/report22-submit/inputs/poc_valid_raw_scanline_rgb.exr ORIG=artifacts/tmp/report22-submit/bin/repro_rawpixel_negative_size_v3412_original ASAN=artifacts/tmp/report22-submit/bin/repro_rawpixel_negative_size_v3412_asan # Positive-size control: too-small positive size is rejected before copying. RAWPIXEL_SIZE=16 RAWPIXEL_MODE=canary "$ORIG" "$POC" # expected rc=0 # Negative-size original-build check: logical canary after the 16-byte boundary changes. RAWPIXEL_SIZE=-1 RAWPIXEL_MODE=canary "$ORIG" "$POC" # expected rc=77 # CANARY_CORRUPTED offset=16 declared_buffer=16 pixelDataSize_after=384 byte=0x10 # ASAN check: physical 16-byte heap allocation receives a 384-byte write. RAWPIXEL_SIZE=-1 RAWPIXEL_MODE=heap RAWPIXEL_ALLOC=16 \ ASAN_OPTIONS='abort_on_error=1:detect_leaks=0:symbolize=1:allocator_may_return_null=1' \ "$ASAN" "$POC" # expected rc=134, AddressSanitizer: heap-buffer-overflow, WRITE of size 384Representative ASAN excerpt from v3.4.13:
ERROR: AddressSanitizer: heap-buffer-overflow WRITE of size 384 at 0x502000000380 thread T0 #1 default_read_func .../OpenEXRCore/internal_posix_file_impl.h:177:14 #2 dispatch_read .../OpenEXRCore/context.c:51:16 #3 exr_read_chunk .../OpenEXRCore/chunk.c:1557:17 #4 Imf_3_4::ScanLineInputFile::rawPixelDataToBuffer(int, char*, int&) const .../src/lib/OpenEXR/ImfScanLineInputFile.cpp:378:13 0x502000000380 is located 0 bytes after 16-byte region [0x502000000370,0x502000000380) SUMMARY: AddressSanitizer: heap-buffer-overflow ... in pread64Impact
A consuming application that accepts untrusted EXR files and passes an unchecked signed size into
rawPixelDataToBuffer()can have file-controlled raw chunk bytes written past the intended caller buffer. The demonstrated impact is memory corruption / out-of-bounds write: the original build overwrites bytes immediately after the declared buffer boundary, and ASAN reports a heap-buffer-overflow write from the raw chunk read path.
Updates
2026-08-11 03:50 CEST
Metadata changes:
- Status for package
openexr: “Plausible”
2026-08-11 03:30 CEST
Metadata changes:
- Status for package
openexr: “New”