Skip to content

GHSA-rw5h-3q4v-c3vc

CVE Information

OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow

Summary

A crafted flat scanline EXR can make the PyOpenEXR wrapper reuse a 2D NumPy array allocated for a literal channel such as left as the destination buffer for coalesced prefixed RGB channels left.R, left.G, and left.B. The wrapper then installs three RGB Slices with xStride = itemsize * 3 into the undersized 2D buffer. Opening the file with the default public API, OpenEXR.File(path), triggers a heap out-of-bounds write during normal pixel decode.

This was revalidated on OpenEXR v3.4.13 and the local current main build. The non-sanitized/original Python extension aborts with heap corruption, and ASAN reports heap-buffer-overflow with a WRITE of size 2 in src/lib/OpenEXRCore/unpack.c:924. The control file containing only left.R/G/B opens cleanly.

Affected and reproduced builds

The local validation matrix is:

target    build   case    rc  classification
v3.4.13   original    control 0   clean
v3.4.13   original    poc 134 original heap corruption abort
v3.4.13   asan    control 0   clean
v3.4.13   asan    poc 134 ASAN heap-buffer-overflow WRITE
v3.4.13   ubsan   control 0   clean
v3.4.13   ubsan   poc 134 original heap corruption abort
v3.4.13   msan    control 1   MSAN import failure (__msan_retval_tls)
v3.4.13   msan    poc 1   MSAN import failure (__msan_retval_tls)
main  original    control 0   clean
main  original    poc 134 original heap corruption abort
main  asan    control 0   clean
main  asan    poc 134 ASAN heap-buffer-overflow WRITE
main  ubsan   control 0   clean
main  ubsan   poc 134 original heap corruption abort

main is still affected in this workspace:

  • main original PoC: rc 134, original heap corruption abort.
  • main ASAN PoC: rc 134, AddressSanitizer: heap-buffer-overflow, WRITE of size 2, sink sources/openexr-main/src/lib/OpenEXRCore/unpack.c:924, PyOpenEXR path sources/openexr-main/src/wrappers/python/PyOpenEXR.cpp:583.

The PoC file also passes exrcheck in this tree:

$ builds/original/bin/exrcheck poc_materials/poc/prefixed_literal_half_left_rgb.exr
 file report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/poc_materials/poc/prefixed_literal_half_left_rgb.exr OK

Root cause details

The vulnerable invariant is in src/wrappers/python/PyOpenEXR.cpp for flat channel loading. With separate_channels=false, each physical EXR channel name is first mapped through channelNameToRGBA().

For v3.4.13:

  • PyOpenEXR.cpp:250-254 starts with the physical EXR channel name and may rewrite left.R, left.G, and left.B to the same Python channel key left.
  • PyOpenEXR.cpp:258-299 allocates a new PyChannel only when the Python key does not already exist. A literal left channel is not in the RGB set, so it creates a 2D array with shape (height, width). For this PoC that buffer is only 32 bytes: 2 * 8 * sizeof(half).
  • PyOpenEXR.cpp:316-344 later reuses the existing channels["left"] object for left.R/G/B, multiplies xStride by three, offsets the lane pointer for G/B, and calls Slice::Make() using the same undersized 2D buffer.
  • PyOpenEXR.cpp:957-1004 maps a prefixed RGB triplet such as left.R/G/B to the prefix key left.

The current local main tree has the same behavior at PyOpenEXR.cpp:478, PyOpenEXR.cpp:482-523, PyOpenEXR.cpp:542-568, and PyOpenEXR.cpp:1181-1228.

The PoC declares these public channel names:

left      HALF
left.B    HALF
left.G    HALF
left.R    HALF

Because ChannelList iteration is sorted, the literal left channel is processed before left.B, left.G, and left.R. The 2D left array already exists when the prefixed RGB channels attempt to coalesce into the same key. There is no validation that the reused PyChannel was originally allocated with a 3-lane RGB shape.

PoC package contents

The cleaned poc.zip should contain only the files needed to reproduce and verify this report:

poc.zip

  • reproduce.sh
  • poc_materials/poc/prefixed_literal_half_left_rgb.exr — crashing PoC; literal left plus left.R/G/B, all HALF.
  • poc_materials/poc/prefixed_rgb_no_literal.exr — clean control with only left.R/G/B.
  • poc_materials/src/repro_prefixed_key_collision.py — default PyOpenEXR reproducer.
  • poc_materials/src/gen_prefixed_key_collision.cpp — generator used to produce the PoC/control from public OpenEXR writer APIs.
  • selected final reproduction logs under poc_materials/logs/.

Reproduction

From the extracted poc.zip directory, point OPENEXR_ROOT at the OpenEXR workspace containing the required v3.4.13 and main Python builds:

OPENEXR_ROOT=/home/br0nzu/openexr/v3.4.13 ./reproduce.sh

From the original report directory inside this workspace, this also works:

bash reproduce.sh

Expected final line:

PASS: prefixed literal/RGB key-collision heap OOB reproduced on v3.4.13 and main original+ASAN; controls clean. MSAN Python extension build currently fails at import on this host.

Impact

A malformed but public EXR file can corrupt heap memory in processes that use the default PyOpenEXR reader API. The demonstrated primitive is a heap out-of-bounds write into/after a NumPy allocation controlled by the wrapper's decoded pixel destination layout. The demonstrated impact is reliable process crash in original builds and sanitizer-confirmed heap buffer overflow write in ASAN builds.

Any application, pipeline tool, or service that accepts untrusted EXR files and opens them with OpenEXR.File(path) without separate_channels=True can be impacted. This report does not prove arbitrary code execution, but heap out-of-bounds writes in image decode paths are security-relevant because crafted media can be supplied by an untrusted party and decoded by automated asset-processing systems.

A robust fix should reject literal channel keys that collide with coalesced RGB/RGBA group keys, or validate that any reused channel entry was allocated with the expected RGB/RGBA shape and matching pixel type before installing additional slices.

Full validation output

reproduce.sh stdout

reproduce_run.out

target    build   case    rc  classification
v3.4.13   original    control 0   clean
v3.4.13   original    poc 134 original heap corruption abort
v3.4.13   asan    control 0   clean
v3.4.13   asan    poc 134 ASAN heap-buffer-overflow WRITE
v3.4.13   ubsan   control 0   clean
v3.4.13   ubsan   poc 134 original heap corruption abort
v3.4.13   msan    control 1   MSAN import failure (__msan_retval_tls)
v3.4.13   msan    poc 1   MSAN import failure (__msan_retval_tls)
main  original    control 0   clean
main  original    poc 134 original heap corruption abort
main  asan    control 0   clean
main  asan    poc 134 ASAN heap-buffer-overflow WRITE
main  ubsan   control 0   clean
main  ubsan   poc 134 original heap corruption abort
PASS: prefixed literal/RGB key-collision heap OOB reproduced on v3.4.13 and main original+ASAN; controls clean. MSAN Python extension build currently fails at import on this host.

reproduce.sh stderr

reproduce_run.err

report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5641 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"
report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5654 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"
report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5662 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"
report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5680 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"
report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5691 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"
report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/reproduce.sh: line 17:  5699 Aborted                 env "${envargs[@]}" timeout --preserve-status 20s python3 "$PY" "$file" > "$prefix.out" 2> "$prefix.err"

v3.4.13 original PoC stdout

reproduce_v3.4.13_original_poc.out

keys ['left']
shape (2, 8) dtype float16 nbytes 32
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 192, 86, 208, 86, 224, 86, 240, 86, 0, 87, 16, 87, 32, 87, 48, 87]
values_first8 [[10.0], [20.0], [30.0], [11.0], [21.0], [31.0], [12.0], [22.0]]
sep_keys ['left', 'left.B', 'left.G', 'left.R']

v3.4.13 original PoC stderr

reproduce_v3.4.13_original_poc.err

double free or corruption (out)
timeout: the monitored command dumped core

v3.4.13 ASAN control stdout

reproduce_v3.4.13_asan_control.out

keys ['left']
shape (2, 8, 3) dtype float16 nbytes 96
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 0, 80, 128, 74, 192, 77, 32, 80, 0, 75, 0, 78, 64, 80, 128, 75, 64, 78, 96, 80, 0, 76, 128, 78, 128, 80, 64, 76, 192, 78, 160, 80, 128, 76, 0, 79, 192, 80, 192, 76, 64, 79, 224, 80, 0, 77, 128, 79, 0, 81, 64, 77, 192, 79, 32, 81, 128, 77, 0, 80, 64, 81, 192, 77, 32, 80, 96, 81, 0, 78, 64, 80, 128, 81, 64, 78, 96, 80, 160, 81]
values_first8 [[10.0, 20.0, 30.0], [11.0, 21.0, 31.0], [12.0, 22.0, 32.0], [13.0, 23.0, 33.0], [14.0, 24.0, 34.0], [15.0, 25.0, 35.0], [16.0, 26.0, 36.0], [17.0, 27.0, 37.0]]
sep_keys ['left.B', 'left.G', 'left.R']

v3.4.13 ASAN control stderr

reproduce_v3.4.13_asan_control.err

(empty)

v3.4.13 ASAN PoC stdout

reproduce_v3.4.13_asan_poc.out

(empty)

v3.4.13 ASAN PoC stderr

reproduce_v3.4.13_asan_poc.err

=================================================================
==5655==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300004d042 at pc 0x799bf02c3113 bp 0x7ffc00876290 sp 0x7ffc00876280
WRITE of size 2 at 0x50300004d042 thread T0
    #0 0x799bf02c3112 in unpack_16bit_4chan /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/unpack.c:924
    #1 0x799bf02accaf in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/decoding.c:664
    #2 0x799bf07838d6 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&) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586
    #3 0x799bf078166c in Imf_3_4::ScanLineInputFile::Data::readPixels(Imf_3_4::FrameBuffer const&, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500
    #4 0x799bf07175b9 in Imf_3_4::InputFile::Data::readPixels(int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXR/ImfInputFile.cpp:458
    #5 0x799bf0a50990 in (anonymous namespace)::PyPart::readPixels(Imf_3_4::MultiPartInputFile&, Imf_3_4::ChannelList const&, std::vector<unsigned long, std::allocator<unsigned long> > const&, std::set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Imath_3_1::Box<Imath_3_1::Vec2<int> > const&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/wrappers/python/PyOpenEXR.cpp:359
    #6 0x799bf0a50990 in (anonymous namespace)::PyFile::PyFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/wrappers/python/PyOpenEXR.cpp:219
    #7 0x799bf0a49450 in _ZN8pybind116detail8initimpl23construct_or_initializeIN12_GLOBAL__N_16PyFileEJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbETnNSt9enable_ifIXsr3std16is_constructibleIT_DpT0_EE5valueEiE4typeELi0EEEPSC_DpOSD_ /usr/include/pybind11/detail/init.h:76
    #8 0x799bf0a49450 in _ZZN8pybind116detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESG_A548_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSJ_DpRKT0_ENKUlRNS0_16value_and_holderES8_bbE_clESS_S8_bb /usr/include/pybind11/detail/init.h:206
    #9 0x799bf0a49450 in _ZNO8pybind116detail15argument_loaderIJRNS0_16value_and_holderENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbEE9call_implIvRZNS0_8initimpl11constructorIJS9_bbEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESL_A548_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSO_DpRKT0_EUlS3_S9_bbE_JLm0ELm1ELm2ELm3EENS0_9void_typeEEESO_OT0_St16integer_sequenceImJXspT1_EEEOT2_ /usr/include/pybind11/detail/../cast.h:1480
    #10 0x799bf0a49450 in _ZNO8pybind116detail15argument_loaderIJRNS0_16value_and_holderENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbEE4callIvNS0_9void_typeERZNS0_8initimpl11constructorIJS9_bbEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESM_A548_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSP_DpRKT0_EUlS3_S9_bbE_EENSO_IXsr3std7is_voidISP_EE5valueESC_E4typeEOT1_ /usr/include/pybind11/detail/../cast.h:1454
    #11 0x799bf0a4905d in _ZZN8pybind1112cpp_function10initializeIZNS_6detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESI_A548_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSL_DpRKT0_EUlRNS2_16value_and_holderESA_bbE_vJSU_SA_bbEJNS_4nameENS_9is_methodENS_7siblingENS2_24is_new_style_constructorESH_SI_SI_SJ_EEEvOSL_PFT0_DpT1_EDpRKT2_ENKUlRNS2_13function_callEE_clES1B_ /usr/include/pybind11/pybind11.h:254
    #12 0x799bf0a4905d in _ZZN8pybind1112cpp_function10initializeIZNS_6detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESI_A548_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSL_DpRKT0_EUlRNS2_16value_and_holderESA_bbE_vJSU_SA_bbEJNS_4nameENS_9is_methodENS_7siblingENS2_24is_new_style_constructorESH_SI_SI_SJ_EEEvOSL_PFT0_DpT1_EDpRKT2_ENUlRNS2_13function_callEE_8__invokeES1B_ /usr/include/pybind11/pybind11.h:224
    #13 0x799bf0a8e99e in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) /usr/include/pybind11/pybind11.h:946
    #14 0x5821ee  (/usr/bin/python3.12+0x5821ee) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #15 0x5492b4 in _PyObject_MakeTpCall (/usr/bin/python3.12+0x5492b4) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #16 0x54cfc6  (/usr/bin/python3.12+0x54cfc6) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #17 0x59e300  (/usr/bin/python3.12+0x59e300) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #18 0x599c52  (/usr/bin/python3.12+0x599c52) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #19 0x799bf0a81357 in pybind11_meta_call /usr/include/pybind11/detail/class.h:187
    #20 0x5492b4 in _PyObject_MakeTpCall (/usr/bin/python3.12+0x5492b4) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #21 0x5d7388 in _PyEval_EvalFrameDefault (/usr/bin/python3.12+0x5d7388) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #22 0x5d58ba in PyEval_EvalCode (/usr/bin/python3.12+0x5d58ba) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #23 0x608b51  (/usr/bin/python3.12+0x608b51) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #24 0x6b5262  (/usr/bin/python3.12+0x6b5262) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #25 0x6b4fc9 in _PyRun_SimpleFileObject (/usr/bin/python3.12+0x6b4fc9) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #26 0x6b4dfe in _PyRun_AnyFileObject (/usr/bin/python3.12+0x6b4dfe) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #27 0x6bce64 in Py_RunMain (/usr/bin/python3.12+0x6bce64) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #28 0x6bc94c in Py_BytesMain (/usr/bin/python3.12+0x6bc94c) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #29 0x799bf542a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #30 0x799bf542a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #31 0x657b04 in _start (/usr/bin/python3.12+0x657b04) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)

0x50300004d042 is located 2 bytes after 32-byte region [0x50300004d020,0x50300004d040)
allocated by thread T0 here:
    #0 0x799bf58fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x799bf26cc65a  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xcc65a) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #2 0x799bf26e4b02  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xe4b02) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #3 0x799bf26e8c55  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xe8c55) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #4 0x799bf2731087  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0x131087) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #5 0x5da85b in _PyEval_EvalFrameDefault (/usr/bin/python3.12+0x5da85b) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #6 0x5d58ba in PyEval_EvalCode (/usr/bin/python3.12+0x5d58ba) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #7 0x608b51  (/usr/bin/python3.12+0x608b51) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #8 0x6b5262  (/usr/bin/python3.12+0x6b5262) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #9 0x6b4fc9 in _PyRun_SimpleFileObject (/usr/bin/python3.12+0x6b4fc9) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #10 0x6b4dfe in _PyRun_AnyFileObject (/usr/bin/python3.12+0x6b4dfe) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #11 0x6bce64 in Py_RunMain (/usr/bin/python3.12+0x6bce64) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #12 0x6bc94c in Py_BytesMain (/usr/bin/python3.12+0x6bc94c) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #13 0x799bf542a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x799bf542a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x657b04 in _start (/usr/bin/python3.12+0x657b04) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/br0nzu/openexr/v3.4.13/sources/openexr-3.4.13/src/lib/OpenEXRCore/unpack.c:924 in unpack_16bit_4chan
Shadow bytes around the buggy address:
  0x50300004cd80: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00
  0x50300004ce00: fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00
  0x50300004ce80: 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd fa fa
  0x50300004cf00: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00
  0x50300004cf80: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
=>0x50300004d000: 00 00 fa fa 00 00 00 00[fa]fa fd fd fd fd fa fa
  0x50300004d080: 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd
  0x50300004d100: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
  0x50300004d180: 00 00 fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x50300004d200: 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd
  0x50300004d280: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 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
==5655==ABORTING
timeout: the monitored command dumped core

v3.4.13 UBSAN PoC stdout

reproduce_v3.4.13_ubsan_poc.out

keys ['left']
shape (2, 8) dtype float16 nbytes 32
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 192, 86, 208, 86, 224, 86, 240, 86, 0, 87, 16, 87, 32, 87, 48, 87]
values_first8 [[10.0], [20.0], [30.0], [11.0], [21.0], [31.0], [12.0], [22.0]]
sep_keys ['left', 'left.B', 'left.G', 'left.R']

v3.4.13 UBSAN PoC stderr

reproduce_v3.4.13_ubsan_poc.err

double free or corruption (out)
timeout: the monitored command dumped core

main original PoC stdout

reproduce_main_original_poc.out

keys ['left']
shape (2, 8) dtype float16 nbytes 32
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 192, 86, 208, 86, 224, 86, 240, 86, 0, 87, 16, 87, 32, 87, 48, 87]
values_first8 [[10.0], [20.0], [30.0], [11.0], [21.0], [31.0], [12.0], [22.0]]
sep_keys ['left', 'left.B', 'left.G', 'left.R']

main original PoC stderr

reproduce_main_original_poc.err

double free or corruption (out)
timeout: the monitored command dumped core

main ASAN control stdout

reproduce_main_asan_control.out

keys ['left']
shape (2, 8, 3) dtype float16 nbytes 96
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 0, 80, 128, 74, 192, 77, 32, 80, 0, 75, 0, 78, 64, 80, 128, 75, 64, 78, 96, 80, 0, 76, 128, 78, 128, 80, 64, 76, 192, 78, 160, 80, 128, 76, 0, 79, 192, 80, 192, 76, 64, 79, 224, 80, 0, 77, 128, 79, 0, 81, 64, 77, 192, 79, 32, 81, 128, 77, 0, 80, 64, 81, 192, 77, 32, 80, 96, 81, 0, 78, 64, 80, 128, 81, 64, 78, 96, 80, 160, 81]
values_first8 [[10.0, 20.0, 30.0], [11.0, 21.0, 31.0], [12.0, 22.0, 32.0], [13.0, 23.0, 33.0], [14.0, 24.0, 34.0], [15.0, 25.0, 35.0], [16.0, 26.0, 36.0], [17.0, 27.0, 37.0]]
sep_keys ['left.B', 'left.G', 'left.R']

main ASAN control stderr

reproduce_main_asan_control.err

(empty)

main ASAN PoC stdout

reproduce_main_asan_poc.out

(empty)

main ASAN PoC stderr

reproduce_main_asan_poc.err

=================================================================
==5692==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300004d042 at pc 0x784025ac3113 bp 0x7ffccf7d8790 sp 0x7ffccf7d8780
WRITE of size 2 at 0x50300004d042 thread T0
    #0 0x784025ac3112 in unpack_16bit_4chan /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/unpack.c:924
    #1 0x784025aaccaf in exr_decoding_run /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/decoding.c:664
    #2 0x784025f87296 in Imf_4_0::(anonymous namespace)::ScanLineProcess::run_decode(_priv_exr_context_t const*, int, Imf_4_0::FrameBuffer const*, int, int, std::vector<Imf_4_0::Slice, std::allocator<Imf_4_0::Slice> > const&) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfScanLineInputFile.cpp:586
    #3 0x784025f8505c in Imf_4_0::ScanLineInputFile::Data::readPixels(Imf_4_0::FrameBuffer const&, int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfScanLineInputFile.cpp:500
    #4 0x784025f1b0a9 in Imf_4_0::InputFile::Data::readPixels(int, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXR/ImfInputFile.cpp:458
    #5 0x78402629de24 in (anonymous namespace)::PyPart::readPixels(Imf_4_0::MultiPartInputFile&, Imf_4_0::ChannelList const&, std::vector<unsigned long, std::allocator<unsigned long> > const&, std::set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Imath_3_1::Box<Imath_3_1::Vec2<int> > const&, bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/wrappers/python/PyOpenEXR.cpp:583
    #6 0x78402629de24 in (anonymous namespace)::PyFile::readPartsFromOpenInput(bool) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/wrappers/python/PyOpenEXR.cpp:443
    #7 0x784026296916 in (anonymous namespace)::PyFile::PyFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, bool, int) /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/wrappers/python/PyOpenEXR.cpp:362
    #8 0x784026296916 in _ZN8pybind116detail8initimpl23construct_or_initializeIN12_GLOBAL__N_16PyFileEJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiETnNSt9enable_ifIXsr3std16is_constructibleIT_DpT0_EE5valueEiE4typeELi0EEEPSC_DpOSD_ /usr/include/pybind11/detail/init.h:76
    #9 0x784026296916 in _ZZN8pybind116detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESG_SG_A641_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSJ_DpRKT0_ENKUlRNS0_16value_and_holderES8_bbiE_clESS_S8_bbi /usr/include/pybind11/detail/init.h:206
    #10 0x784026296916 in _ZNO8pybind116detail15argument_loaderIJRNS0_16value_and_holderENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiEE9call_implIvRZNS0_8initimpl11constructorIJS9_bbiEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESL_SL_A641_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSO_DpRKT0_EUlS3_S9_bbiE_JLm0ELm1ELm2ELm3ELm4EENS0_9void_typeEEESO_OT0_St16integer_sequenceImJXspT1_EEEOT2_ /usr/include/pybind11/detail/../cast.h:1480
    #11 0x784026296916 in _ZNO8pybind116detail15argument_loaderIJRNS0_16value_and_holderENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiEE4callIvNS0_9void_typeERZNS0_8initimpl11constructorIJS9_bbiEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESM_SM_A641_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSP_DpRKT0_EUlS3_S9_bbiE_EENSO_IXsr3std7is_voidISP_EE5valueESC_E4typeEOT1_ /usr/include/pybind11/detail/../cast.h:1454
    #12 0x784026296296 in _ZZN8pybind1112cpp_function10initializeIZNS_6detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESI_SI_A641_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSL_DpRKT0_EUlRNS2_16value_and_holderESA_bbiE_vJSU_SA_bbiEJNS_4nameENS_9is_methodENS_7siblingENS2_24is_new_style_constructorESH_SI_SI_SI_SJ_EEEvOSL_PFT0_DpT1_EDpRKT2_ENKUlRNS2_13function_callEE_clES1B_ /usr/include/pybind11/pybind11.h:254
    #13 0x784026296296 in _ZZN8pybind1112cpp_function10initializeIZNS_6detail8initimpl11constructorIJNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbbiEE7executeINS_6class_IN12_GLOBAL__N_16PyFileEJEEEJNS_3argENS_5arg_vESI_SI_A641_cETnNSt9enable_ifIXntsrT_9has_aliasEiE4typeELi0EEEvRSL_DpRKT0_EUlRNS2_16value_and_holderESA_bbiE_vJSU_SA_bbiEJNS_4nameENS_9is_methodENS_7siblingENS2_24is_new_style_constructorESH_SI_SI_SI_SJ_EEEvOSL_PFT0_DpT1_EDpRKT2_ENUlRNS2_13function_callEE_8__invokeES1B_ /usr/include/pybind11/pybind11.h:224
    #14 0x7840262e772e in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) /usr/include/pybind11/pybind11.h:946
    #15 0x5821ee  (/usr/bin/python3.12+0x5821ee) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #16 0x5492b4 in _PyObject_MakeTpCall (/usr/bin/python3.12+0x5492b4) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #17 0x54cfc6  (/usr/bin/python3.12+0x54cfc6) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #18 0x59e300  (/usr/bin/python3.12+0x59e300) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #19 0x599c52  (/usr/bin/python3.12+0x599c52) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #20 0x7840262da0e7 in pybind11_meta_call /usr/include/pybind11/detail/class.h:187
    #21 0x5492b4 in _PyObject_MakeTpCall (/usr/bin/python3.12+0x5492b4) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #22 0x5d7388 in _PyEval_EvalFrameDefault (/usr/bin/python3.12+0x5d7388) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #23 0x5d58ba in PyEval_EvalCode (/usr/bin/python3.12+0x5d58ba) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #24 0x608b51  (/usr/bin/python3.12+0x608b51) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #25 0x6b5262  (/usr/bin/python3.12+0x6b5262) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #26 0x6b4fc9 in _PyRun_SimpleFileObject (/usr/bin/python3.12+0x6b4fc9) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #27 0x6b4dfe in _PyRun_AnyFileObject (/usr/bin/python3.12+0x6b4dfe) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #28 0x6bce64 in Py_RunMain (/usr/bin/python3.12+0x6bce64) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #29 0x6bc94c in Py_BytesMain (/usr/bin/python3.12+0x6bc94c) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #30 0x78402ac2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #31 0x78402ac2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #32 0x657b04 in _start (/usr/bin/python3.12+0x657b04) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)

0x50300004d042 is located 2 bytes after 32-byte region [0x50300004d020,0x50300004d040)
allocated by thread T0 here:
    #0 0x78402b0fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x784027ecc65a  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xcc65a) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #2 0x784027ee4b02  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xe4b02) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #3 0x784027ee8c55  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0xe8c55) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #4 0x784027f31087  (/usr/lib/python3/dist-packages/numpy/core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so+0x131087) (BuildId: 4e90c45f411fba3236c21490996071d3467336f7)
    #5 0x5da85b in _PyEval_EvalFrameDefault (/usr/bin/python3.12+0x5da85b) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #6 0x5d58ba in PyEval_EvalCode (/usr/bin/python3.12+0x5d58ba) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #7 0x608b51  (/usr/bin/python3.12+0x608b51) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #8 0x6b5262  (/usr/bin/python3.12+0x6b5262) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #9 0x6b4fc9 in _PyRun_SimpleFileObject (/usr/bin/python3.12+0x6b4fc9) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #10 0x6b4dfe in _PyRun_AnyFileObject (/usr/bin/python3.12+0x6b4dfe) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #11 0x6bce64 in Py_RunMain (/usr/bin/python3.12+0x6bce64) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #12 0x6bc94c in Py_BytesMain (/usr/bin/python3.12+0x6bc94c) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)
    #13 0x78402ac2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x78402ac2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x657b04 in _start (/usr/bin/python3.12+0x657b04) (BuildId: 7aa38bef4bcd63064ecbd05a66f6120026526b74)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/br0nzu/openexr/v3.4.13/sources/openexr-main/src/lib/OpenEXRCore/unpack.c:924 in unpack_16bit_4chan
Shadow bytes around the buggy address:
  0x50300004cd80: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00
  0x50300004ce00: fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00
  0x50300004ce80: 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd fa fa
  0x50300004cf00: 00 00 00 00 fa fa 00 00 00 00 fa fa 00 00 00 00
  0x50300004cf80: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
=>0x50300004d000: 00 00 fa fa 00 00 00 00[fa]fa fd fd fd fd fa fa
  0x50300004d080: 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd
  0x50300004d100: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
  0x50300004d180: 00 00 fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x50300004d200: 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd fd fd
  0x50300004d280: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 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
==5692==ABORTING
timeout: the monitored command dumped core

main UBSAN PoC stdout

reproduce_main_ubsan_poc.out

keys ['left']
shape (2, 8) dtype float16 nbytes 32
raw_first96 [0, 73, 0, 77, 128, 79, 128, 73, 64, 77, 192, 79, 0, 74, 128, 77, 192, 86, 208, 86, 224, 86, 240, 86, 0, 87, 16, 87, 32, 87, 48, 87]
values_first8 [[10.0], [20.0], [30.0], [11.0], [21.0], [31.0], [12.0], [22.0]]
sep_keys ['left', 'left.B', 'left.G', 'left.R']

main UBSAN PoC stderr

reproduce_main_ubsan_poc.err

free(): invalid next size (fast)
timeout: the monitored command dumped core

v3.4.13 MSAN import-failure control stdout

reproduce_v3.4.13_msan_control.out

(empty)

v3.4.13 MSAN import-failure control stderr

reproduce_v3.4.13_msan_control.err

Traceback (most recent call last):
  File "/home/br0nzu/openexr/v3.4.13/report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/poc_materials/src/repro_prefixed_key_collision.py", line 9, in <module>
    import OpenEXR
ImportError: /home/br0nzu/openexr/v3.4.13/builds/python_msan/src/lib/OpenEXRCore/libOpenEXRCore-3_4.so.33: undefined symbol: __msan_retval_tls

v3.4.13 MSAN import-failure PoC stdout

reproduce_v3.4.13_msan_poc.out

(empty)

v3.4.13 MSAN import-failure PoC stderr

reproduce_v3.4.13_msan_poc.err

Traceback (most recent call last):
  File "/home/br0nzu/openexr/v3.4.13/report/55. OpenEXR v3.4.13 PyOpenEXR prefixed literal RGB key collision heap buffer overflow/poc_materials/src/repro_prefixed_key_collision.py", line 9, in <module>
    import OpenEXR
ImportError: /home/br0nzu/openexr/v3.4.13/builds/python_msan/src/lib/OpenEXRCore/libOpenEXRCore-3_4.so.33: undefined symbol: __msan_retval_tls