{"id":"PYSEC-2026-1746","summary":"OpenEXR Out of Bounds Heap Read due to Bad Pointer Arithmetic in LossyDctDecoder_execute","details":"### Summary\nThe 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.\n\n### Details\n\nIn 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:\n\n```cpp\n// no-op conversion to linear\nfor (int y = 8 * blocky; y \u003c 8 * blocky + maxY; ++y)\n{\n    __m128i* restrict dst = (__m128i *) chanData[comp]-\u003e_rows[y];\n    __m128i const * restrict src = (__m128i const *)&rowBlock[comp][(y & 0x7) * 8];\n\n    for (int blockx = 0; blockx \u003c numFullBlocksX; ++blockx)\n    {\n        _mm_storeu_si128 (dst, _mm_loadu_si128 (src)); //\n\n        src += 8 * 8; // \u003c--- si128 pointer incremented as a uint16_t\n        dst += 8;\n    }\n}\n```\n\nThe 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.\n\n### PoC\n\nIn 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.\n\nNOTE: please download the `dwadecoder_crash.exr` file from the following link: \n\nhttps://github.com/ShielderSec/poc/tree/main/CVE-2025-48072\n\n1. Compile the `exrcheck` binary in a macOS or GNU/Linux machine with ASAN.\n2. Open the `dwadecoder_crash.exr` file with the following command:\n\n```\nexrcheck dwadecoder_crash.exr\n```\n\n3. Notice that `exrcheck` crashes with ASAN stack-trace.\n\n```\n==2297956==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x52500000a110 at pc 0x55e590db7bf1 bp 0x7fff948bb110 sp 0x7fff948bb108\nREAD of size 16 at 0x52500000a110 thread T0\n    #0 0x55e590db7bf0 in LossyDctDecoder_execute /root/openexr/src/lib/OpenEXRCore/internal_dwa_decoder.h:650:48\n    #1 0x55e590dae18d in DwaCompressor_uncompress /root/openexr/src/lib/OpenEXRCore/internal_dwa_compressor.h:1132:30\n    #2 0x55e590da9960 in internal_exr_undo_dwaa /root/openexr/src/lib/OpenEXRCore/internal_dwa.c:202:18\n    #3 0x55e590d42d03 in exr_uncompress_chunk /root/openexr/src/lib/OpenEXRCore/compression.c:516:14\n    #4 0x55e590dc3132 in exr_decoding_run /root/openexr/src/lib/OpenEXRCore/decoding.c:580:14\n    #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\u003cImf_3_4::Slice, std::allocator\u003cImf_3_4::Slice\u003e\u003e const&) /root/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:585:23\n    #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\n    #7 0x55e590c73c97 in Imf_3_4::ScanLineInputFile::readPixels(int, int) /root/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:306:12\n    #8 0x55e590c73c97 in Imf_3_4::InputFile::Data::readPixels(int, int) /root/openexr/src/lib/OpenEXR/ImfInputFile.cpp:446:20\n    #9 0x55e590c1f92f in Imf_3_4::InputFile::readPixels(int) /root/openexr/src/lib/OpenEXR/ImfInputFile.cpp:228:12\n    #10 0x55e590c1f92f in Imf_3_4::InputPart::readPixels(int) /root/openexr/src/lib/OpenEXR/ImfInputPart.cpp:70:11\n    #11 0x55e590c1f92f in bool Imf_3_4::(anonymous namespace)::readScanline\u003cImf_3_4::InputPart\u003e(Imf_3_4::InputPart&, bool, bool) /root/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:239:20\n    #12 0x55e590c1f92f in Imf_3_4::(anonymous namespace)::readMultiPart(Imf_3_4::MultiPartInputFile&, bool, bool) /root/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:879:28\n    #13 0x55e590c155af in bool Imf_3_4::(anonymous namespace)::runChecks\u003cchar const*\u003e(char const*&, bool, bool) /root/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1132:21\n    #14 0x55e590c155af in Imf_3_4::checkOpenEXRFile(char const*, bool, bool, bool) /root/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1796:19\n    #15 0x55e590ba5abe in exrCheck(char const*, bool, bool, bool, bool) /root/openexr/src/bin/exrcheck/main.cpp:96:16\n    #16 0x55e590ba6fbe in main /root/openexr/src/bin/exrcheck/main.cpp:164:29\n    #17 0x7f4259e2a1c9 in __libc_start_call_main csu/../sysdeps/npthttps://gitlab.com/qemu-project/qemu/-/issuesl/libc_start_call_main.h:58:16\n    #18 0x7f4259e2a28a in __libc_start_main csu/../csu/libc-start.c:360:3\n    #19 0x55e590ac67d4 in _start (/root/openexr/_build_afl_asan/bin/exrcheck+0x1d87d4) (BuildId: 49c2658b2f9ddef9)\n\n0x52500000a110 is located 752 bytes after 9504-byte region [0x525000007900,0x525000009e20)\nallocated by thread T0 here:\n    #0 0x55e590b61623 in malloc (/root/openexr/_build_afl_asan/bin/exrcheck+0x273623) (BuildId: 49c2658b2f9ddef9)\n    #1 0x55e590db11b1 in LossyDctDecoder_execute /root/openexr/src/lib/OpenEXRCore/internal_dwa_decoder.h:324:22\n    #2 0x55e590dae18d in DwaCompressor_uncompress /root/openexr/src/lib/OpenEXRCore/internal_dwa_compressor.h:1132:30\n    #3 0x55e590da9960 in internal_exr_undo_dwaa /root/openexr/src/lib/OpenEXRCore/internal_dwa.c:202:18\n    #4 0x55e590d42d03 in exr_uncompress_chunk /root/openexr/src/lib/OpenEXRCore/compression.c:516:14\n```\n\n### Impact\nAn attacker could crash the application and in some scenarios also leak data, such as sensitive information or memory addresses that might be used to bypass exploitation mitigations like ASLR.","aliases":["CVE-2025-48072","GHSA-4r7w-q3jg-ff43"],"modified":"2026-07-07T17:47:08.250309625Z","published":"2026-07-07T16:02:59.723169Z","references":[{"type":"WEB","url":"https://github.com/AcademySoftwareFoundation/openexr/security/advisories/GHSA-4r7w-q3jg-ff43"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-48072"},{"type":"WEB","url":"https://github.com/AcademySoftwareFoundation/openexr/commit/2d09449427b13a05f7c31a98ab2c4347c23db361"},{"type":"PACKAGE","url":"https://github.com/AcademySoftwareFoundation/openexr"},{"type":"WEB","url":"https://github.com/AcademySoftwareFoundation/openexr/releases/tag/v3.3.3"},{"type":"WEB","url":"https://github.com/ShielderSec/poc/tree/main/CVE-2025-48072"},{"type":"PACKAGE","url":"https://pypi.org/project/openexr"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-4r7w-q3jg-ff43"}],"affected":[{"package":{"name":"openexr","ecosystem":"PyPI","purl":"pkg:pypi/openexr"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.3.2"},{"fixed":"3.3.3"}]}],"versions":["3.3.2"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/openexr/PYSEC-2026-1746.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:H/SC:N/SI:N/SA:N"}]}