diff -urNp a/src/lib/OpenEXRUtil/ImfImageChannel.cpp b/src/lib/OpenEXRUtil/ImfImageChannel.cpp --- a/src/lib/OpenEXRUtil/ImfImageChannel.cpp 2026-06-25 11:16:51.018229425 +0200 +++ b/src/lib/OpenEXRUtil/ImfImageChannel.cpp 2026-06-25 11:18:53.946853720 +0200 @@ -13,6 +13,9 @@ #include "ImfImageLevel.h" #include +#include +#include + using namespace IMATH_NAMESPACE; using namespace IEX_NAMESPACE; using namespace std; @@ -54,6 +57,13 @@ ImageChannel::channel () const void ImageChannel::resize () { + if (_xSampling < 1 || _ySampling < 1) + { + throw ArgExc ( + "The x and y sampling rates for an image channel must be at " + "least 1."); + } + const Box2i& dataWindow = level().dataWindow(); if (dataWindow.min.x % _xSampling || dataWindow.min.y % _ySampling) @@ -75,7 +85,17 @@ ImageChannel::resize () _pixelsPerRow = width / _xSampling; _pixelsPerColumn = height / _ySampling; - _numPixels = _pixelsPerRow * _pixelsPerColumn; + + const uint64_t w64 = static_cast (_pixelsPerRow); + const uint64_t h64 = static_cast (_pixelsPerColumn); + const uint64_t maxPixels = + static_cast (std::numeric_limits::max ()); + if (w64 != 0 && h64 > maxPixels / w64) + { + throw ArgExc ("Image channel dimensions too large."); + } + + _numPixels = static_cast (w64 * h64); } diff -urNp a/src/test/OpenEXRUtilTest/CMakeLists.txt b/src/test/OpenEXRUtilTest/CMakeLists.txt --- a/src/test/OpenEXRUtilTest/CMakeLists.txt 2026-06-25 11:16:50.949047862 +0200 +++ b/src/test/OpenEXRUtilTest/CMakeLists.txt 2026-06-25 11:20:16.694600831 +0200 @@ -9,6 +9,8 @@ add_executable(OpenEXRUtilTest testDeepImage.h testIO.cpp testIO.h + testImageChannel.cpp + testImageChannel.h ) target_include_directories(OpenEXRUtilTest PRIVATE ../OpenEXRTest) target_link_libraries(OpenEXRUtilTest OpenEXR::OpenEXRUtil) @@ -30,4 +32,5 @@ define_openexr_util_tests( testFlatImage testDeepImage testIO + testImageChannel ) diff -urNp a/src/test/OpenEXRUtilTest/main.cpp b/src/test/OpenEXRUtilTest/main.cpp --- a/src/test/OpenEXRUtilTest/main.cpp 2026-06-25 11:16:50.949047862 +0200 +++ b/src/test/OpenEXRUtilTest/main.cpp 2026-06-25 11:21:10.550436231 +0200 @@ -11,6 +11,7 @@ #include "OpenEXRConfigInternal.h" #include "testFlatImage.h" +#include "testImageChannel.h" #include "testDeepImage.h" #include "testIO.h" #include "tmpDir.h" @@ -87,6 +88,7 @@ main (int argc, char *argv[]) TEST (testFlatImage); TEST (testDeepImage); TEST (testIO); + TEST (testImageChannel); // NB: If you add a test here, make sure to enumerate it in the // CMakeLists.txt so it runs as part of the test suite diff -urNp a/src/test/OpenEXRUtilTest/testImageChannel.cpp b/src/test/OpenEXRUtilTest/testImageChannel.cpp --- a/src/test/OpenEXRUtilTest/testImageChannel.cpp 1970-01-01 01:00:00.000000000 +0100 +++ b/src/test/OpenEXRUtilTest/testImageChannel.cpp 2026-06-25 11:21:58.464289792 +0200 @@ -0,0 +1,82 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) Contributors to the OpenEXR Project. +// + +#ifdef NDEBUG +# undef NDEBUG +#endif + +#include "testImageChannel.h" + +#include "ImfFlatImage.h" + +#include +#include + +#include +#include +#include +#include + +using namespace OPENEXR_IMF_NAMESPACE; +using namespace IMATH_NAMESPACE; +using namespace IEX_NAMESPACE; +using namespace std; + +namespace +{ + +// +// Sampling must be >= 1. ImageChannel::resize() rejects non-positive +// sampling before any modulo or division (so zero cannot invoke undefined +// behavior). Other invalid cases are still caught later in resize(). +// + +void +expectArgExcOnInsert (const char* label, int xSampling, int ySampling) +{ + FlatImage img (Box2i (V2i (0, 0), V2i (3, 3))); + + try + { + img.insertChannel ("bad", HALF, xSampling, ySampling, false); + cerr << "ERROR -- " << label << ": expected ArgExc, insert succeeded" + << endl; + assert (false); + } + catch (const ArgExc& e) + { + // Thrown at the start of ImageChannel::resize() so % and / never use + // a zero sampling rate (undefined behavior). + assert (strstr (e.what (), "at least 1") != nullptr); + } +} + +} // namespace + +void +testImageChannel (const string& tempDir) +{ + (void) tempDir; + + try + { + cout << "Testing ImageChannel sampling constraints" << endl; + + expectArgExcOnInsert ("negative xSampling", -1, 1); + expectArgExcOnInsert ("negative ySampling", 1, -1); + expectArgExcOnInsert ("negative x and y sampling", -1, -1); + + expectArgExcOnInsert ("zero xSampling", 0, 1); + expectArgExcOnInsert ("zero ySampling", 1, 0); + expectArgExcOnInsert ("zero x and y sampling", 0, 0); + + cout << "ok\n" << endl; + } + catch (const std::exception& e) + { + cerr << "ERROR -- caught exception: " << e.what () << endl; + assert (false); + } +} diff -urNp a/src/test/OpenEXRUtilTest/testImageChannel.h b/src/test/OpenEXRUtilTest/testImageChannel.h --- a/src/test/OpenEXRUtilTest/testImageChannel.h 1970-01-01 01:00:00.000000000 +0100 +++ b/src/test/OpenEXRUtilTest/testImageChannel.h 2026-06-25 11:22:42.531155111 +0200 @@ -0,0 +1,8 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) Contributors to the OpenEXR Project. +// + +#include + +void testImageChannel (const std::string& tempDir);