GitHub Security Advisory: GHSA-4r7w-q3jg-ff43
Package: OpenEXR (PIP)
Severity: MODERATE (CVSS 0.0)
CWE: CWE-125: Out-of-bounds Read
Vulnerable versions: = 3.3.2
Patched in: 3.3.3

Summary: OpenEXR Out of Bounds Heap Read due to Bad Pointer Arithmetic in LossyDctDecoder_execute

Details:
### Summary
The OpenEXRCore code 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.

### Details

In the `LossyDctDecoder_execute` function (from `src/lib/OpenEXRCore/internal_dwa_decoder.h`, when SSE2 is enabled), the following code is used to copy data from the chunks:

```cpp
// no-op conversion to linear
for (int y = 8 * blocky; y < 8 * blocky + maxY; ++y)
{
    __m128i* restrict dst = (__m128i *) chanData[comp]->_rows[y];
    __m128i const * restrict src = (__m128i const *)&rowBlock[comp][(y & 0x7) * 8];

    for (int blockx = 0; blockx < numFullBlocksX; ++blockx)
    {
        _mm_storeu_si128 (dst, _mm_loadu_si128 (src)); //

        src += 8 * 8; // <--- si128 pointer incremented as a uint16_t
        dst += 8;
    }
}
```

The issue arises because the `src` pointer, which is a `si128` pointer, is incremented by `8*8`, as if it were a `uint16_t` pointer (64 * uint16_t == 128 bytes). In non-block aligned chunks (width/height not a multiple of 8), this can cause `src` to point past the boundaries of the chunk.

### PoC

In order to reproduce the PoC with fidelity and avoid undefined behaviors, it is necessary to enable ASAN (and SSE2). Otherwise the out-of-bound read will not be detected until its side-effect causes a crash.

NOTE: please download the `dwadecoder_crash.exr` file from the following link: 

https://github.com/ShielderSec/poc/tree/main/CVE-2025-48072

1. Compile the `exrcheck` binary in a macOS or GNU/Linux machine with ASAN.
2. Open the `dwadecoder_crash.exr` file with the following command:

```
exrcheck dwadecoder_crash.exr
```

3. Notice that `exrcheck` crashes with ASAN stack-trace.

```
==2297956==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x52500000a110 at pc 0x55e590db7bf1 bp 0x7fff948bb110 sp 0x7fff948bb108
READ of size 16 at 0x52500000a110 thread T0
    #0 0x55e590db7bf0 in LossyDctDecoder_execute /root/openexr/src/lib/OpenEXRCore/internal_dwa_decoder.h:650:48
    #1 0x55e590dae18d in DwaCompressor_uncompress /root/openexr/src/lib/OpenEXRCore/internal_dwa_compressor.h:1132:30
    #2 0x55e590da9960 in internal_exr_undo_dwaa /root/openexr/src/lib/OpenEXRCore/internal_dwa.c:202:18
    #3 0x55e590d42d03 in exr_uncompress_chunk /root/openexr/src/lib/OpenEXRCore/compression.c:516:14
    #4 0x55e590dc3132 in exr_decoding_run /root/openexr/src/lib/OpenEXRCore/decoding.c:580:14
    #5 0x55e590c7d78f in Imf_3_4::(anonymous namespace)::ScanLineProcess::run_decode(_priv_exr_context_t const*, int, Imf_3_4::FrameBuffer const*, int, int, std::vector<Imf_3_4::Slice, std::allocator<Imf_3_4::Slice>> const&) /root/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:585:23
    #6 0x55e590c83ed7 in Imf_3_4::ScanLineInputFile::Data::readPixels(Imf_3_4::FrameBuffer const&, int, int) /root/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:499:21
    #7 0x55e590c73c97 in Imf_3_4::ScanLineInputFile::readPixe