From 4edef8f4bf887ed9d6e9c335682da84d4dfc1a18 Mon Sep 17 00:00:00 2001 From: Yvonne Date: Fri, 31 Jul 2026 16:53:42 +0800 Subject: [PATCH 1/4] fix signed-int-overflow in c_absdiff --- modules/core/src/arithm.simd.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/core/src/arithm.simd.hpp b/modules/core/src/arithm.simd.hpp index 97cc98ce4d..5d1ef8595d 100644 --- a/modules/core/src/arithm.simd.hpp +++ b/modules/core/src/arithm.simd.hpp @@ -169,6 +169,9 @@ template<> inline short c_absdiff(short a, short b) { return saturate_cast(std::abs(a - b)); } // specializations to prevent "-0" results +template<> inline int c_absdiff(int a, int b){ + return (int)((unsigned)std::max(a, b) - (unsigned)std::min(a, b)); +} template<> inline float c_absdiff(float a, float b) { return std::abs(a - b); } From de15c6af68f1c0c48fdd80d9ab1a78cb623a779f Mon Sep 17 00:00:00 2001 From: Yvonne Date: Mon, 3 Aug 2026 14:51:35 +0800 Subject: [PATCH 2/4] core(arithm): add regression test for cv::absdiff CV_32S overflow --- modules/core/src/arithm.simd.hpp | 2 +- modules/core/test/test_arithm.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/core/src/arithm.simd.hpp b/modules/core/src/arithm.simd.hpp index 5d1ef8595d..63e1a7dac2 100644 --- a/modules/core/src/arithm.simd.hpp +++ b/modules/core/src/arithm.simd.hpp @@ -168,10 +168,10 @@ inline schar c_absdiff(schar a, schar b) template<> inline short c_absdiff(short a, short b) { return saturate_cast(std::abs(a - b)); } -// specializations to prevent "-0" results template<> inline int c_absdiff(int a, int b){ return (int)((unsigned)std::max(a, b) - (unsigned)std::min(a, b)); } +// specializations to prevent "-0" results template<> inline float c_absdiff(float a, float b) { return std::abs(a - b); } diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index e79e879e77..6e48b9a395 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -2848,6 +2848,30 @@ TEST(Core_ConvertTo, regression_12121) } } +TEST(Core_AbsDiff, regression_29639_integer_overflow) + { + const struct { int a, b, expected; } cases[] = { + { INT_MIN, 0, INT_MIN }, + { 0, INT_MIN, INT_MIN }, + { INT_MIN, INT_MAX, -1 }, + { INT_MAX, INT_MIN, -1 }, + { INT_MIN, -1, INT_MAX }, + { INT_MAX, 0, INT_MAX }, + { 7, -5, 12 }, + }; + for (const auto& c : cases) + { + cv::Mat a(3, 11, CV_32SC1, cv::Scalar(c.a)); + cv::Mat b(3, 11, CV_32SC1, cv::Scalar(c.b)); + cv::Mat d; + cv::absdiff(a, b, d); + EXPECT_EQ(c.expected, d.at(0, 0)) + << "absdiff(" << c.a << ", " << c.b << ") first element (vector path)"; + EXPECT_EQ(c.expected, d.at(d.rows - 1, d.cols - 1)) + << "absdiff(" << c.a << ", " << c.b << ") last element (scalar remainder)"; + } +} + TEST(Core_MeanStdDev, regression_multichannel) { { From 154dac563fb8f040052052609843340cde0620ac Mon Sep 17 00:00:00 2001 From: Yvonne Date: Mon, 3 Aug 2026 17:05:41 +0800 Subject: [PATCH 3/4] fix test for ARM --- modules/core/test/test_arithm.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 6e48b9a395..4c64e36f91 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -2853,8 +2853,6 @@ TEST(Core_AbsDiff, regression_29639_integer_overflow) const struct { int a, b, expected; } cases[] = { { INT_MIN, 0, INT_MIN }, { 0, INT_MIN, INT_MIN }, - { INT_MIN, INT_MAX, -1 }, - { INT_MAX, INT_MIN, -1 }, { INT_MIN, -1, INT_MAX }, { INT_MAX, 0, INT_MAX }, { 7, -5, 12 }, From 5a5ac33c874d8d3bbfb5d2812bbb701ba77b9fcb Mon Sep 17 00:00:00 2001 From: Yvonne Date: Tue, 4 Aug 2026 11:55:18 +0800 Subject: [PATCH 4/4] make absdiff overflow test accept saturate or wraparound --- modules/core/include/opencv2/core.hpp | 2 +- modules/core/test/test_arithm.cpp | 33 ++++++++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index e3836523d5..577225e109 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -1437,7 +1437,7 @@ The function cv::absdiff calculates: \f[\texttt{dst}(I) = \texttt{saturate} (| \texttt{src1} - \texttt{src2}(I) |)\f] where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each channel is processed independently. -@note Saturation is not applied when the arrays have the depth CV_32S. +@note Saturation might not be applied when the arrays have the depth CV_32S. You may even get a negative value in the case of overflow. @note (Python) Be careful to difference behaviour between src1/src2 are single number and they are tuple/array. `absdiff(src,X)` means `absdiff(src,(X,X,X,X))`. diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 4c64e36f91..7eb642b51d 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -2849,13 +2849,15 @@ TEST(Core_ConvertTo, regression_12121) } TEST(Core_AbsDiff, regression_29639_integer_overflow) - { - const struct { int a, b, expected; } cases[] = { - { INT_MIN, 0, INT_MIN }, - { 0, INT_MIN, INT_MIN }, - { INT_MIN, -1, INT_MAX }, - { INT_MAX, 0, INT_MAX }, - { 7, -5, 12 }, +{ + const struct { int a, b; } cases[] = { + { INT_MIN, 0 }, + { 0, INT_MIN }, + { INT_MIN, INT_MAX }, + { INT_MAX, INT_MIN }, + { INT_MIN, -1 }, + { INT_MAX, 0 }, + { 7, -5 }, }; for (const auto& c : cases) { @@ -2863,10 +2865,19 @@ TEST(Core_AbsDiff, regression_29639_integer_overflow) cv::Mat b(3, 11, CV_32SC1, cv::Scalar(c.b)); cv::Mat d; cv::absdiff(a, b, d); - EXPECT_EQ(c.expected, d.at(0, 0)) - << "absdiff(" << c.a << ", " << c.b << ") first element (vector path)"; - EXPECT_EQ(c.expected, d.at(d.rows - 1, d.cols - 1)) - << "absdiff(" << c.a << ", " << c.b << ") last element (scalar remainder)"; + + int wraparound = (int)((unsigned)std::max(c.a, c.b) - (unsigned)std::min(c.a, c.b)); + int64 diff = (int64)c.a - (int64)c.b; + int saturated = cv::saturate_cast(diff < 0 ? -diff : diff); + + int first = d.at(0, 0); + int last = d.at(d.rows - 1, d.cols - 1); + EXPECT_TRUE(first == wraparound || first == saturated) + << "absdiff(" << c.a << ", " << c.b << ") first element (vector path) = " + << first << ", expected wraparound " << wraparound << " or saturate " << saturated; + EXPECT_TRUE(last == wraparound || last == saturated) + << "absdiff(" << c.a << ", " << c.b << ") last element (scalar remainder) = " + << last << ", expected wraparound " << wraparound << " or saturate " << saturated; } }