AcademySoftwareFoundation / openexr

The OpenEXR project provides the specification and reference implementation of the EXR file format, the professional-grade image storage format of the motion picture industry.
http://www.openexr.com/
BSD 3-Clause "New" or "Revised" License
1.62k stars 609 forks source link

possible valgrind error in Imf_3_1::copyFromFrameBuffer() triggered by ctlrender outputting 16-bit EXR #1322

Closed michaeldsmith closed 1 year ago

michaeldsmith commented 1 year ago

hi - I'm the maintainer of https://github.com/ampas/CTL/

I recently added a test for ctlrender to output 16-bit EXR and 32-bit EXR files. I ran valgrind on these tests, and the 16-bit EXR output test results in a valgrind error while the 32-bit EXR output test does not result in valgrind error. I've filed issue (https://github.com/ampas/CTL/issues/109) in CTL repo to track this, the valgrind test that is part of the CI is now failing.

I don't believe the valgrind issue is due to code in the the CTL repo, I think it may be due to issue in OpenEXR code. I will post the steps to reproduce the issue with OpenEXR 3.1 and OpenEXR 2.5 below.

michaeldsmith commented 1 year ago

Here are the steps to reproduce the ctlrender valgrind issue using OpenEXR 3.1:

git clone https://github.com/ampas/CTL.git
cd CTL
docker build --rm -f Dockerfile_openexr3 -t ctl:latest .
docker run -it --rm ctl:latest
cd build
rm -R *
cmake .. -DCMAKE_BUILD_TYPE=Debug
make
make install
cd unittest/ctlrender/
valgrind -s --error-exitcode=1 --leak-check=full --track-origins=yes --show-leak-kinds=all ../../ctlrender/ctlrender -force -format exr16 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr
==846== Memcheck, a memory error detector
==846== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==846== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==846== Command: ../../ctlrender/ctlrender -force -format exr16 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr
==846==
==846== Invalid read of size 2
==846==    at 0x49163C3: Imf_3_1::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_3_1::Compressor::Format, Imf_3_1::PixelType) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x4924109: Imf_3_1::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x522F3E0: IlmThread_3_1::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x522F6F6: IlmThread_3_1::ThreadPool::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x49249B8: Imf_3_1::OutputFile::writePixels(int) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x1C36DA: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==  Address 0x5749754 is 4 bytes after a block of size 194,400 alloc'd
==846==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==846==    by 0x1C41DF: ctl::dpx::fb<Imath_3_2::half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==846==    by 0x1C3515: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==
==846== Invalid read of size 2
==846==    at 0x49163B0: Imf_3_1::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_3_1::Compressor::Format, Imf_3_1::PixelType) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x4924109: Imf_3_1::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x522F3E0: IlmThread_3_1::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x522F6F6: IlmThread_3_1::ThreadPool::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x49249B8: Imf_3_1::OutputFile::writePixels(int) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x1C36DA: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==  Address 0x574975c is 12 bytes after a block of size 194,400 alloc'd
==846==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==846==    by 0x1C41DF: ctl::dpx::fb<Imath_3_2::half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==846==    by 0x1C3515: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==
==846==
==846== HEAP SUMMARY:
==846==     in use at exit: 0 bytes in 0 blocks
==846==   total heap usage: 1,038 allocs, 1,038 frees, 13,541,155 bytes allocated
==846==
==846== All heap blocks were freed -- no leaks are possible
==846==
==846== ERROR SUMMARY: 24300 errors from 2 contexts (suppressed: 0 from 0)
==846==
==846== 12051 errors in context 1 of 2:
==846== Invalid read of size 2
==846==    at 0x49163C3: Imf_3_1::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_3_1::Compressor::Format, Imf_3_1::PixelType) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x4924109: Imf_3_1::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x522F3E0: IlmThread_3_1::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x522F6F6: IlmThread_3_1::ThreadPool::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x49249B8: Imf_3_1::OutputFile::writePixels(int) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x1C36DA: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==  Address 0x5749754 is 4 bytes after a block of size 194,400 alloc'd
==846==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==846==    by 0x1C41DF: ctl::dpx::fb<Imath_3_2::half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==846==    by 0x1C3515: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==
==846==
==846== 12249 errors in context 2 of 2:
==846== Invalid read of size 2
==846==    at 0x49163B0: Imf_3_1::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_3_1::Compressor::Format, Imf_3_1::PixelType) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x4924109: Imf_3_1::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x522F3E0: IlmThread_3_1::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x522F6F6: IlmThread_3_1::ThreadPool::addTask(IlmThread_3_1::Task*) (in /usr/local/lib/libIlmThread-3_1.so.30.5.1)
==846==    by 0x49249B8: Imf_3_1::OutputFile::writePixels(int) (in /usr/local/lib/libOpenEXR-3_1.so.30.5.1)
==846==    by 0x1C36DA: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==  Address 0x574975c is 12 bytes after a block of size 194,400 alloc'd
==846==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==846==    by 0x1C41DF: ctl::dpx::fb<Imath_3_2::half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==846==    by 0x1C3515: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==846==    by 0x1C37FF: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==846==    by 0x1BE4D6: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==846==    by 0x1B7748: main (main.cc:678)
==846==
==846== ERROR SUMMARY: 24300 errors from 2 contexts (suppressed: 0 from 0)
michaeldsmith commented 1 year ago

Here are the steps to reproduce the ctlrender valgrind issue using OpenEXR 2.5:

git clone https://github.com/ampas/CTL.git
cd CTL
docker build --rm -f Dockerfile -t ctl:latest .
docker run -it --rm ctl:latest
cd build
rm -R *
cmake .. -DCMAKE_BUILD_TYPE=Debug
make
make install
cd unittest/ctlrender/
valgrind -s --error-exitcode=1 --leak-check=full --track-origins=yes --show-leak-kinds=all ../../ctlrender/ctlrender -force -format exr16 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr
==785== Memcheck, a memory error detector
==785== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==785== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==785== Command: ../../ctlrender/ctlrender -force -format exr16 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr     
==785==
==785== Invalid read of size 2
==785==    at 0x48E59A3: Imf_2_5::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_2_5::Compressor::Format, Imf_2_5::PixelType) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x48BDAC9: Imf_2_5::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x5204450: IlmThread_2_5::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)   
==785==    by 0x5204726: IlmThread_2_5::ThreadPool::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)
==785==    by 0x48BE378: Imf_2_5::OutputFile::writePixels(int) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x1C267A: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==  Address 0x57156e4 is 4 bytes after a block of size 194,400 alloc'd
==785==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==785==    by 0x1C309D: ctl::dpx::fb<half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==785==    by 0x1C24B5: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==
==785== Invalid read of size 2
==785==    at 0x48E5990: Imf_2_5::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_2_5::Compressor::Format, Imf_2_5::PixelType) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x48BDAC9: Imf_2_5::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x5204450: IlmThread_2_5::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)   
==785==    by 0x5204726: IlmThread_2_5::ThreadPool::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)
==785==    by 0x48BE378: Imf_2_5::OutputFile::writePixels(int) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x1C267A: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==  Address 0x57156ec is 12 bytes after a block of size 194,400 alloc'd
==785==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==785==    by 0x1C309D: ctl::dpx::fb<half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==785==    by 0x1C24B5: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==
==785== 
==785== HEAP SUMMARY:
==785==     in use at exit: 0 bytes in 0 blocks
==785==   total heap usage: 1,037 allocs, 1,037 frees, 13,541,107 bytes allocated
==785==
==785== All heap blocks were freed -- no leaks are possible
==785==
==785== ERROR SUMMARY: 24300 errors from 2 contexts (suppressed: 0 from 0)
==785==
==785== 12051 errors in context 1 of 2:
==785== Invalid read of size 2
==785==    at 0x48E59A3: Imf_2_5::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_2_5::Compressor::Format, Imf_2_5::PixelType) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x48BDAC9: Imf_2_5::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x5204450: IlmThread_2_5::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)   
==785==    by 0x5204726: IlmThread_2_5::ThreadPool::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)
==785==    by 0x48BE378: Imf_2_5::OutputFile::writePixels(int) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x1C267A: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==  Address 0x57156e4 is 4 bytes after a block of size 194,400 alloc'd
==785==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==785==    by 0x1C309D: ctl::dpx::fb<half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==785==    by 0x1C24B5: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==
==785==
==785== 12249 errors in context 2 of 2:
==785== Invalid read of size 2
==785==    at 0x48E5990: Imf_2_5::copyFromFrameBuffer(char*&, char const*&, char const*, unsigned long, Imf_2_5::Compressor::Format, Imf_2_5::PixelType) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x48BDAC9: Imf_2_5::(anonymous namespace)::LineBufferTask::execute() (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x5204450: IlmThread_2_5::(anonymous namespace)::NullThreadPoolProvider::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)   
==785==    by 0x5204726: IlmThread_2_5::ThreadPool::addTask(IlmThread_2_5::Task*) (in /usr/local/lib/libIlmThread-2_5.so.25.0.7)
==785==    by 0x48BE378: Imf_2_5::OutputFile::writePixels(int) (in /usr/local/lib/libIlmImf-2_5.so.26.0.0)
==785==    by 0x1C267A: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:247)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==  Address 0x57156ec is 12 bytes after a block of size 194,400 alloc'd
==785==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==785==    by 0x1C309D: ctl::dpx::fb<half>::init(unsigned int, unsigned int, unsigned int) (dpx.tcc:85)
==785==    by 0x1C24B5: exr_write16(char const*, float, ctl::dpx::fb<float> const&, Compression*) (exr_file.cc:228)
==785==    by 0x1C279D: exr_write(char const*, float, ctl::dpx::fb<float> const&, format_t*, Compression*) (exr_file.cc:257)
==785==    by 0x1BD476: transform(char const*, char const*, float, float, format_t*, Compression*, std::__cxx11::list<ctl_operation_t, std::allocator<ctl_operation_t> > const&, std::__cxx11::list<ctl_parameter_t, std::allocator<ctl_parameter_t> > const&) (transform.cc:843)
==785==    by 0x1B6786: main (main.cc:678)
==785==
==785== ERROR SUMMARY: 24300 errors from 2 contexts (suppressed: 0 from 0)
michaeldsmith commented 1 year ago

using -format exr32 to set ctlrender output to 32-bit EXR does not cause any valgrind issue with OpenEXR 3.1 or OpenEXR 2.5

valgrind -s --error-exitcode=1 --leak-check=full --track-origins=yes --show-leak-kinds=all ../../ctlrender/ctlrender -force -format exr32 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr

==847== Memcheck, a memory error detector
==847== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==847== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==847== Command: ../../ctlrender/ctlrender -force -format exr32 -ctl ../../../unittest/ctlrender/unity.ctl ../../../unittest/ctlrender/bars_photoshop.exr out.exr
==847==
==847==
==847== HEAP SUMMARY:
==847==     in use at exit: 0 bytes in 0 blocks
==847==   total heap usage: 1,030 allocs, 1,030 frees, 13,224,179 bytes allocated
==847==
==847== All heap blocks were freed -- no leaks are possible
==847==
==847== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
peterhillman commented 1 year ago

I'm not at a keyboard now to check but I think the setFrameBuffer in the CTL exr_write16 assumes you always have exactly four channels (since it treats the buffer as an Rgba array) Using the more verbose approach that write32 uses to set up the FrameBuffer might work better

michaeldsmith commented 1 year ago

thanks @peterhillman - I re-wrote the ctlrender exr_write16() function similar to exr_write32() as you suggested and it has resolved the valgrind issue. Thanks again for the suggestion. If you have a few minutes and want to review my pull request, it is here https://github.com/ampas/CTL/pull/116

michaeldsmith commented 1 year ago

Also, I wanted to note that while researching this issue, I found this ancient 2003 email [1] from Florian that I think suggests the RgbaOutputFile and setFrameBuffer did support RGB in the past. If it that is no longer the case and if they only support the WRITE_RGBA option, it might make sense to throw a warning or exception if the WRITE_RGB value is used with the RgbaOutputFileconstructor.

Here is Florian's code snippet from that email using WRITE_RGB

Header header (width, height);
RgbaOutputFile out (fileName, header, WRITE_RGB);
out.setFrameBuffer (&pixels[0][0], 1, width);
out.writePixels (height);

[1] https://lists.aswf.io/g/openexr-dev/topic/31940200?p=Created,,,20,2,0,0::recentpostdate%2Fsticky,,,20,1,0,31940200

It also seems there are several example uses of RgbaOutputFile with WRITE_RGB are included in the current OpenEXR main branch, for example:

There is one use of WRITE_RGB in this file at line 32: \bazel\example\main.cpp

and 4 uses of WRITE_RGB in this file at lines 188, 211, 326 and 364 \src\test\OpenEXRTest\testStandardAttributes.cpp

Vertexwahn commented 1 year ago

Just for the records regarding \bazel\example\main.cpp

Here is a slightly modified version of the file that also reads in the written .exr file and outputs some data: https://github.com/Vertexwahn/Piper/blob/main/BazelDemos/third_party_libraries/cpp/OpenEXR/main.cpp ->

        for (Imf::ChannelList::ConstIterator channel = exrChannels.begin();
                channel != exrChannels.end();
                channel++) {
            std::cout << channel.name() << std::endl;
        }

Gives me

width: 100 height: 100
A
B
G
R

when RgbaOutputFile is used with WRITE_RGBA.

WRITE_RGB outputs:

width: 100 height: 100
B
G
R

WRITE_RGB Seems to work - does not have an alpha channel, whereas WRITE_RGBA has an alpha channel

michaeldsmith commented 1 year ago

@Vertexwahn it would be interesting to see if valgrind also reports errors when using WRITE_RGB with \bazel\example\main.cpp versus using WRITE_RGBA with \bazel\example\main.cpp

peterhillman commented 1 year ago

I think this makes sense, and I don't think anything is wrong with the library. That setFrameBuffer method always assumes that the data is stored as an Rgba array: WRITE_RGB and WRITE_RGBA change which channels are written, not which channels are assumed to be in the provided array. So, even if you only write RGB, you still need to provide four half floats per pixel to use that setFrameBuffer method, and the alpha value slot will be ignored if you specify WRITE_RGB.

The valgrind error arose because a three-halfs-per-pixel array was cast to an Rgba array, causing the library to read beyond the end of the array.

michaeldsmith commented 1 year ago

thanks again @peterhillman - closing the issue since there is noting wrong with the OpenEXR library