From 59f2be9f03bbfdf582f9600b18fc4da8de46b807 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Fri, 4 Sep 2026 13:54:49 +0200 Subject: [PATCH] Fix int usage for pixbuf when double is asked for. When chtype is double (used by bicubic64fC1 .. bicubic64fC4), std::is_same_v evaluated to false. As a result, buftype erroneously defaulted to int, and pixbuf was allocated as int pixbuf[NCHANNELS][4]. That could trigger out of bound integer computations. --- modules/imgproc/src/warp_kernels.simd.hpp | 103 +++++++++++----------- modules/imgproc/test/test_imgwarp.cpp | 24 +++++ 2 files changed, 76 insertions(+), 51 deletions(-) diff --git a/modules/imgproc/src/warp_kernels.simd.hpp b/modules/imgproc/src/warp_kernels.simd.hpp index bda168095e..250cdb47c1 100644 --- a/modules/imgproc/src/warp_kernels.simd.hpp +++ b/modules/imgproc/src/warp_kernels.simd.hpp @@ -7170,67 +7170,67 @@ static void bicubicFetchPixels(const _Tp* src, size_t srcstep, Size size, int cn #undef BICUBIC_PROCESS_INLIERS_C1_SCALAR #define BICUBIC_PROCESS_INLIERS_C1_SCALAR(row) \ srcrow = (const chtype*)((const uint8_t*)src + row*srcstep + tl_ofs); \ - V0 = float(srcrow[0]); \ - V1 = float(srcrow[1]); \ - V2 = float(srcrow[2]); \ - V3 = float(srcrow[3]); \ + V0 = acctype(srcrow[0]); \ + V1 = acctype(srcrow[1]); \ + V2 = acctype(srcrow[2]); \ + V3 = acctype(srcrow[3]); \ BICUBIC_UPDATE_ACC(acc_r, V0, V1, V2, V3, wy##row) #undef BICUBIC_PROCESS_INLIERS_C2_SCALAR #define BICUBIC_PROCESS_INLIERS_C2_SCALAR(row) \ srcrow = (const chtype*)((const uint8_t*)src + row*srcstep + tl_ofs); \ - V0 = float(srcrow[0]); \ - V1 = float(srcrow[2]); \ - V2 = float(srcrow[4]); \ - V3 = float(srcrow[6]); \ + V0 = acctype(srcrow[0]); \ + V1 = acctype(srcrow[2]); \ + V2 = acctype(srcrow[4]); \ + V3 = acctype(srcrow[6]); \ BICUBIC_UPDATE_ACC(acc_r, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[1]); \ - V1 = float(srcrow[3]); \ - V2 = float(srcrow[5]); \ - V3 = float(srcrow[7]); \ + V0 = acctype(srcrow[1]); \ + V1 = acctype(srcrow[3]); \ + V2 = acctype(srcrow[5]); \ + V3 = acctype(srcrow[7]); \ BICUBIC_UPDATE_ACC(acc_g, V0, V1, V2, V3, wy##row) #undef BICUBIC_PROCESS_INLIERS_C3_SCALAR #define BICUBIC_PROCESS_INLIERS_C3_SCALAR(row) \ srcrow = (const chtype*)((const uint8_t*)src + row*srcstep + tl_ofs); \ - V0 = float(srcrow[0]); \ - V1 = float(srcrow[3]); \ - V2 = float(srcrow[6]); \ - V3 = float(srcrow[9]); \ + V0 = acctype(srcrow[0]); \ + V1 = acctype(srcrow[3]); \ + V2 = acctype(srcrow[6]); \ + V3 = acctype(srcrow[9]); \ BICUBIC_UPDATE_ACC(acc_r, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[1]); \ - V1 = float(srcrow[4]); \ - V2 = float(srcrow[7]); \ - V3 = float(srcrow[10]); \ + V0 = acctype(srcrow[1]); \ + V1 = acctype(srcrow[4]); \ + V2 = acctype(srcrow[7]); \ + V3 = acctype(srcrow[10]); \ BICUBIC_UPDATE_ACC(acc_g, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[2]); \ - V1 = float(srcrow[5]); \ - V2 = float(srcrow[8]); \ - V3 = float(srcrow[11]); \ + V0 = acctype(srcrow[2]); \ + V1 = acctype(srcrow[5]); \ + V2 = acctype(srcrow[8]); \ + V3 = acctype(srcrow[11]); \ BICUBIC_UPDATE_ACC(acc_b, V0, V1, V2, V3, wy##row) #undef BICUBIC_PROCESS_INLIERS_C4_SCALAR #define BICUBIC_PROCESS_INLIERS_C4_SCALAR(row) \ srcrow = (const chtype*)((const uint8_t*)src + row*srcstep + tl_ofs); \ - V0 = float(srcrow[0]); \ - V1 = float(srcrow[4]); \ - V2 = float(srcrow[8]); \ - V3 = float(srcrow[12]); \ + V0 = acctype(srcrow[0]); \ + V1 = acctype(srcrow[4]); \ + V2 = acctype(srcrow[8]); \ + V3 = acctype(srcrow[12]); \ BICUBIC_UPDATE_ACC(acc_r, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[1]); \ - V1 = float(srcrow[5]); \ - V2 = float(srcrow[9]); \ - V3 = float(srcrow[13]); \ + V0 = acctype(srcrow[1]); \ + V1 = acctype(srcrow[5]); \ + V2 = acctype(srcrow[9]); \ + V3 = acctype(srcrow[13]); \ BICUBIC_UPDATE_ACC(acc_g, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[2]); \ - V1 = float(srcrow[6]); \ - V2 = float(srcrow[10]); \ - V3 = float(srcrow[14]); \ + V0 = acctype(srcrow[2]); \ + V1 = acctype(srcrow[6]); \ + V2 = acctype(srcrow[10]); \ + V3 = acctype(srcrow[14]); \ BICUBIC_UPDATE_ACC(acc_b, V0, V1, V2, V3, wy##row); \ - V0 = float(srcrow[3]); \ - V1 = float(srcrow[7]); \ - V2 = float(srcrow[11]); \ - V3 = float(srcrow[15]); \ + V0 = acctype(srcrow[3]); \ + V1 = acctype(srcrow[7]); \ + V2 = acctype(srcrow[11]); \ + V3 = acctype(srcrow[15]); \ BICUBIC_UPDATE_ACC(acc_a, V0, V1, V2, V3, wy##row) template @@ -7242,7 +7242,8 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, float A = params ? *params : defaultA; constexpr int BPP = int(NCHANNELS*sizeof(dst[0])); - using buftype = std::conditional_t, float, int>; + using buftype = std::conditional_t, chtype, int>; + using acctype = std::conditional_t, double, float>; using pixtype = std::conditional_t>; pixtype bval = borderVal ? *(pixtype*)borderVal : pixtype(); @@ -7250,7 +7251,7 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, for (int i = 0; i < len; i++, dst += NCHANNELS) { buftype pixbuf[NCHANNELS][4]; int tl_x, tl_y, tl_ofs, goodx[4]; - float V0, V1, V2, V3; + acctype V0, V1, V2, V3; float wx0, wx1, wx2, wx3, wy0, wy1, wy2, wy3; float xs = srcx[i], ys = srcy[i]; @@ -7260,14 +7261,14 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, if (code > 0) { const chtype* srcrow; if constexpr (NCHANNELS == 1) { - float acc_r = 0.f; + acctype acc_r = 0; BICUBIC_PROCESS_INLIERS_C1_SCALAR(0); BICUBIC_PROCESS_INLIERS_C1_SCALAR(1); BICUBIC_PROCESS_INLIERS_C1_SCALAR(2); BICUBIC_PROCESS_INLIERS_C1_SCALAR(3); dst[0] = saturate_cast(acc_r); } else if constexpr (NCHANNELS == 2) { - float acc_r = 0.f, acc_g = 0.f; + acctype acc_r = 0, acc_g = 0; BICUBIC_PROCESS_INLIERS_C2_SCALAR(0); BICUBIC_PROCESS_INLIERS_C2_SCALAR(1); BICUBIC_PROCESS_INLIERS_C2_SCALAR(2); @@ -7275,7 +7276,7 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, dst[0] = saturate_cast(acc_r); dst[1] = saturate_cast(acc_g); } else if constexpr (NCHANNELS == 3) { - float acc_r = 0.f, acc_g = 0.f, acc_b = 0.f; + acctype acc_r = 0, acc_g = 0, acc_b = 0; BICUBIC_PROCESS_INLIERS_C3_SCALAR(0); BICUBIC_PROCESS_INLIERS_C3_SCALAR(1); BICUBIC_PROCESS_INLIERS_C3_SCALAR(2); @@ -7284,7 +7285,7 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, dst[1] = saturate_cast(acc_g); dst[2] = saturate_cast(acc_b); } else if constexpr (NCHANNELS == 4) { - float acc_r = 0.f, acc_g = 0.f, acc_b = 0.f, acc_a = 0.f; + acctype acc_r = 0, acc_g = 0, acc_b = 0, acc_a = 0; BICUBIC_PROCESS_INLIERS_C4_SCALAR(0); BICUBIC_PROCESS_INLIERS_C4_SCALAR(1); BICUBIC_PROCESS_INLIERS_C4_SCALAR(2); @@ -7300,7 +7301,7 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, } continue; } else { - float acc[NCHANNELS] = {}; + acctype acc[NCHANNELS] = {}; float wys[] = {wy0, wy1, wy2, wy3}; const chtype* defVal = borderType == BORDER_TRANSPARENT ? dst : borderVal; for (int row = 0; row < 4; row++) { @@ -7308,10 +7309,10 @@ static void bicubicRef(const float* srcx, const float* srcy, int len, goodx, row, &pixbuf[0][0], 1, borderType, defVal); float wy = wys[row]; for (int c = 0; c < NCHANNELS; c++) { - V0 = float(pixbuf[c][0]); - V1 = float(pixbuf[c][1]); - V2 = float(pixbuf[c][2]); - V3 = float(pixbuf[c][3]); + V0 = acctype(pixbuf[c][0]); + V1 = acctype(pixbuf[c][1]); + V2 = acctype(pixbuf[c][2]); + V3 = acctype(pixbuf[c][3]); BICUBIC_UPDATE_ACC(acc[c], V0, V1, V2, V3, wy); } } diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 6975c31a3b..7fcc7235e8 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1123,6 +1123,30 @@ static void rotation2affine(float scale, float angle, float cx, float cy, float* M[3] = -scale*sa; M[4] = scale*ca; M[5] = scale*(cx*sa - cy*ca) + cy; } +TEST(Imgproc_Warping, Bicubic64F) { + // 1. Values exceeding INT_MAX: triggers UBSan float-cast-overflow if buftype is int + { + cv::Mat src(10, 10, CV_64FC1, cv::Scalar(1e100)); + cv::Mat dst; + cv::Matx23f M(1.f, 0.f, 0.f, 0.f, 1.f, 0.f); + EXPECT_NO_THROW(cv::warpAffine(src, dst, M, cv::Size(20, 20), + cv::INTER_CUBIC, cv::BORDER_CONSTANT, cv::Scalar(0.0))); + ASSERT_EQ(dst.type(), CV_64FC1); + EXPECT_DOUBLE_EQ(dst.at(5, 5), 1e100); + } + // 2. Fractional double values: ensures boundary pixels are not truncated to int + { + cv::Mat src(10, 10, CV_64FC1, cv::Scalar(1.5)); + cv::Mat dst; + cv::Matx23f M(1.f, 0.f, 0.f, 0.f, 1.f, 0.f); + cv::warpAffine(src, dst, M, cv::Size(20, 20), + cv::INTER_CUBIC, cv::BORDER_CONSTANT, cv::Scalar(1.5)); + ASSERT_EQ(dst.type(), CV_64FC1); + // Boundary pixels (x=9, y=9) previously truncated (int)1.5 -> 1.0; now they preserve 1.5: + EXPECT_DOUBLE_EQ(dst.at(9, 9), 1.5); + } +} + TEST(Imgproc_Warping, DISABLED_playground) { int imgtype = CV_32F;