OpenEXR's Inaccurate Pointer Arithmetic can Cause an Out of Bounds Heap
Description
OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. Version 3.3.2 is vulnerable to a heap-based buffer overflow during a read operation due to bad pointer math when decompressing DWAA-packed scan-line EXR files with a maliciously forged chunk. This is fixed in version 3.3.3.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
OpenEXR 3.3.2 suffers from a heap-buffer-overflow in DWAA decompression due to pointer arithmetic error, fixed in 3.3.3.
Vulnerability
Description
CVE-2025-48072 is a heap-based buffer overflow vulnerability in OpenEXR version 3.3.2, specifically in the LossyDctDecoder_execute function within internal_dwa_decoder.h when SSE2 is enabled. The root cause is incorrect pointer arithmetic when decompressing DWAA-packed scan-line EXR files: the src pointer of type __m128i* is incremented by 8*8 (64), which is the step size for a uint16_t* pointer, not for a 16-byte SIMD pointer. This miscalculation causes the source pointer to advance past the allocated chunk boundary for non-block-aligned image dimensions (width/height not a multiple of 8), leading to an out-of-bounds read [1][2].
Exploitation and
Attack Surface
To trigger the vulnerability, an attacker must provide a specially crafted EXR file that utilizes the DWAA compression scheme and has dimensions that are not multiples of 8. The file can be opened by any application that uses OpenEXR Core to decode the image, such as the exrcheck utility. Exploitation does not require authentication; it is a classic file-format parsing vulnerability. A proof-of-concept (PoC) file named dwadecoder_crash.exr has been published, and it reliably reproduces the heap-buffer-overflow when compiled with AddressSanitizer (ASAN) and SSE2 enabled [2][4].
Impact
A successful exploit could result in a read out-of-bounds on the heap, potentially leaking sensitive memory contents or causing an application crash (denial of service). While the issue is classified as a read overflow, under certain conditions it could be leveraged for further memory corruption. The vulnerability is present in the core library (OpenEXRCore), not just the C++ binding [2].
Mitigation
The vulnerability is patched in OpenEXR version 3.3.3. Users should upgrade immediately. The OpenEXR project emphasizes robustness and security as key priorities, and the fix addresses the pointer increment logic to properly handle __m128i* arithmetic [1][2][3].
- NVD - CVE-2025-48072
- Out of Bounds Heap Read due to Bad Pointer Arithmetic in LossyDctDecoder_execute
- GitHub - AcademySoftwareFoundation/openexr: The OpenEXR project provides the specification and reference implementation of the EXR file format, the professional-grade image storage format of the motion picture industry.
- poc/CVE-2025-48072 at main · ShielderSec/poc
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
OpenEXRPyPI | >= 3.3.2, < 3.3.3 | 3.3.3 |
Affected products
2- AcademySoftwareFoundation/openexrv5Range: >= 3.3.2, < 3.3.3
Patches
12d09449427b1Fix bad pointer math
3 files changed · +30 −12
src/lib/OpenEXRCore/internal_dwa_compressor.h+10 −0 modified@@ -854,6 +854,7 @@ DwaCompressor_uncompress ( if (version > 2) { return EXR_ERR_BAD_CHUNK_LEADER; } rv = DwaCompressor_setupChannelData (me); + if (rv != EXR_ERR_SUCCESS) { return rv; } // // Uncompress the UNKNOWN data into _planarUncBuffer[UNKNOWN] @@ -1079,6 +1080,8 @@ DwaCompressor_uncompress ( packedAcBufferEnd += decoder._packedAcCount * sizeof (uint16_t); packedDcBufferEnd += decoder._packedDcCount * sizeof (uint16_t); + + totalAcUncompressedCount -= decoder._packedAcCount; totalDcUncompressedCount -= decoder._packedDcCount; me->_channelData[rChan].processed = 1; @@ -1101,6 +1104,12 @@ DwaCompressor_uncompress ( if (cd->processed) continue; + if (chan->width == 0 || chan->height == 0) + { + cd->processed = 1; + continue; + } + switch (cd->compression) { case LOSSY_DCT: @@ -1138,6 +1147,7 @@ DwaCompressor_uncompress ( packedDcBufferEnd += (size_t) decoder._packedDcCount * sizeof (uint16_t); + totalAcUncompressedCount -= decoder._packedAcCount; totalDcUncompressedCount -= decoder._packedDcCount; if (rv != EXR_ERR_SUCCESS) { return rv; } }
src/lib/OpenEXRCore/internal_dwa_decoder.h+12 −12 modified@@ -320,19 +320,12 @@ LossyDctDecoder_execute ( // Allocate a temp aligned buffer to hold a rows worth of full // 8x8 half-float blocks // - rowBlockHandle = alloc_fn ( (size_t) numComp * (size_t) numBlocksX * 64 * sizeof (uint16_t) + _SSE_ALIGNMENT); if (!rowBlockHandle) return EXR_ERR_OUT_OF_MEMORY; - rowBlock[0] = (uint16_t*) rowBlockHandle; - - for (int i = 0; i < _SSE_ALIGNMENT; ++i) - { - if (((uintptr_t) (rowBlockHandle + i) & _SSE_ALIGNMENT_MASK) == 0) - rowBlock[0] = (uint16_t*) (rowBlockHandle + i); - } + rowBlock[0] = (uint16_t*) simd_align_pointer (rowBlockHandle); for (int comp = 1; comp < numComp; ++comp) rowBlock[comp] = rowBlock[comp - 1] + numBlocksX * 64; @@ -649,8 +642,8 @@ LossyDctDecoder_execute ( { _mm_storeu_si128 (dst, _mm_loadu_si128 (src)); - src += 8 * 8; - dst += 8; + ++dst; + src += 8; } } } @@ -720,9 +713,16 @@ LossyDctDecoder_execute ( dst += 8 * numFullBlocksX; - for (int x = 0; x < maxX; ++x) + if (d->_toLinear) + { + for (int x = 0; x < maxX; ++x) + { + *dst++ = d->_toLinear[*src++]; + } + } + else { - *dst++ = d->_toLinear[*src++]; + memcpy (dst, src, maxX * sizeof(uint16_t)); } } }
src/lib/OpenEXRCore/internal_dwa_simd.h+8 −0 modified@@ -68,6 +68,14 @@ __extension__ extern __inline float32x4x2_t } #endif +static inline uint8_t *simd_align_pointer (uint8_t* ptr) +{ + return ptr + + ((_SSE_ALIGNMENT - (((uintptr_t)ptr) & _SSE_ALIGNMENT_MASK)) & + _SSE_ALIGNMENT_MASK); +} + + // // Color space conversion, Inverse 709 CSC, Y'CbCr -> R'G'B' //
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-4r7w-q3jg-ff43ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-48072ghsaADVISORY
- github.com/AcademySoftwareFoundation/openexr/commit/2d09449427b13a05f7c31a98ab2c4347c23db361ghsax_refsource_MISCWEB
- github.com/AcademySoftwareFoundation/openexr/releases/tag/v3.3.3ghsax_refsource_MISCWEB
- github.com/AcademySoftwareFoundation/openexr/security/advisories/GHSA-4r7w-q3jg-ff43ghsax_refsource_CONFIRMWEB
- github.com/ShielderSec/poc/tree/main/CVE-2025-48072ghsaWEB
News mentions
0No linked articles in our index yet.