VYPR
Moderate severityNVD Advisory· Published Jul 31, 2025· Updated Jul 31, 2025

OpenEXR's Inaccurate Pointer Arithmetic can Cause an Out of Bounds Heap

CVE-2025-48072

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].

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.

PackageAffected versionsPatched versions
OpenEXRPyPI
>= 3.3.2, < 3.3.33.3.3

Affected products

2
  • Openexr/Openexrllm-fuzzy
    Range: <=3.3.2
  • AcademySoftwareFoundation/openexrv5
    Range: >= 3.3.2, < 3.3.3

Patches

1
2d09449427b1

Fix bad pointer math

https://github.com/AcademySoftwareFoundation/openexrKimball ThurstonFeb 9, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.