Fix int usage for pixbuf when double is asked for.

When chtype is double (used by bicubic64fC1 .. bicubic64fC4),
std::is_same_v<double, float> 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.
This commit is contained in:
Vincent Rabaud
2026-09-04 13:54:49 +02:00
parent 3a718750a4
commit 59f2be9f03
2 changed files with 76 additions and 51 deletions

View File

@@ -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<typename chtype, int NCHANNELS>
@@ -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<std::is_same_v<chtype, float>, float, int>;
using buftype = std::conditional_t<std::is_floating_point_v<chtype>, chtype, int>;
using acctype = std::conditional_t<std::is_same_v<chtype, double>, double, float>;
using pixtype = std::conditional_t<NCHANNELS == 1, chtype, Vec<chtype, NCHANNELS>>;
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<chtype>(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<chtype>(acc_r);
dst[1] = saturate_cast<chtype>(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<chtype>(acc_g);
dst[2] = saturate_cast<chtype>(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);
}
}

View File

@@ -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<double>(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<double>(9, 9), 1.5);
}
}
TEST(Imgproc_Warping, DISABLED_playground)
{
int imgtype = CV_32F;