From 733495747661ab3d2a95a379fe8dd03fa9dc53d2 Mon Sep 17 00:00:00 2001 From: Jesus Armando Anaya Date: Sun, 21 Jun 2026 16:39:40 -0700 Subject: [PATCH] core(ocl): fix out-of-bounds read and SIGFPE in predictOptimalVectorWidth for new depths predictOptimalVectorWidth() built its vectorWidths table with 8 entries (depths CV_8U..CV_16F), but checkOptimalVectorWidth() indexes it by depth. The 5.x depths CV_16BF, CV_Bool, CV_64U, CV_64S and CV_32U therefore read past the end of the array; the garbage value can slip past the ckercn <= 0 guard, and the divider normalization loop then shifts the divider to zero, so "offsets[i] % dividers[i]" raises SIGFPE. The failure is allocation dependent and shows up as a sequence-dependent crash, e.g. in the OpenCL Norm tests for CV_32U (cv::norm on a UMat reaches this via ocl_sum, which calls predictOptimalVectorWidth before its own depth guard bails out). Size the table to CV_DEPTH_MAX so every depth is in bounds and map the new fixed-size integer depths to their natural OpenCL vector widths; CV_16BF has no OpenCL vector type and stays scalar. Add a regression test covering all depths. --- modules/core/src/ocl.cpp | 15 +++++++++++---- modules/core/test/ocl/test_arithm.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 5aca984378..e07c07d318 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -7229,10 +7229,17 @@ int predictOptimalVectorWidth(InputArray src1, InputArray src2, InputArray src3, { const ocl::Device & d = ocl::Device::getDefault(); - int vectorWidths[] = { d.preferredVectorWidthChar(), d.preferredVectorWidthChar(), - d.preferredVectorWidthShort(), d.preferredVectorWidthShort(), - d.preferredVectorWidthInt(), d.preferredVectorWidthFloat(), - d.preferredVectorWidthDouble(), d.preferredVectorWidthHalf() }; + // Indexed by depth: must span CV_DEPTH_MAX. bf16 has no OpenCL vector type. + int vectorWidths[CV_DEPTH_MAX] = { + d.preferredVectorWidthChar(), d.preferredVectorWidthChar(), // CV_8U, CV_8S + d.preferredVectorWidthShort(), d.preferredVectorWidthShort(), // CV_16U, CV_16S + d.preferredVectorWidthInt(), d.preferredVectorWidthFloat(), // CV_32S, CV_32F + d.preferredVectorWidthDouble(), d.preferredVectorWidthHalf(), // CV_64F, CV_16F + 1, // CV_16BF + d.preferredVectorWidthChar(), // CV_Bool + d.preferredVectorWidthLong(), d.preferredVectorWidthLong(), // CV_64U, CV_64S + d.preferredVectorWidthInt() // CV_32U + }; // if the device says don't use vectors if (vectorWidths[0] == 1) diff --git a/modules/core/test/ocl/test_arithm.cpp b/modules/core/test/ocl/test_arithm.cpp index cbf31ff3a0..ad15ee93eb 100644 --- a/modules/core/test/ocl/test_arithm.cpp +++ b/modules/core/test/ocl/test_arithm.cpp @@ -2124,6 +2124,30 @@ OCL_TEST(Normalize, DISABLED_regression_5876_inplace_change_type) EXPECT_EQ(0, cvtest::norm(um, uresult, NORM_INF)); } +// Regression: predictOptimalVectorWidth() must handle every depth without +// reading past its per-depth table. +OCL_TEST(Arithm, predictOptimalVectorWidth_all_depths) +{ + if (!cv::ocl::useOpenCL()) + return; + + const int depths[] = { CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F, + CV_16F, CV_16BF, CV_Bool, CV_64U, CV_64S, CV_32U }; + for (size_t i = 0; i < sizeof(depths) / sizeof(depths[0]); ++i) + { + const int depth = depths[i]; + for (int cn = 1; cn <= 4; ++cn) + { + const int type = CV_MAKE_TYPE(depth, cn); + UMat a(11, 13, type), b(11, 13, type); + EXPECT_GE(cv::ocl::predictOptimalVectorWidth(a), 1) + << "depth=" << depth << " cn=" << cn; + EXPECT_GE(cv::ocl::predictOptimalVectorWidth(a, b), 1) + << "depth=" << depth << " cn=" << cn; + } + } +} + } } // namespace opencv_test::ocl #endif // HAVE_OPENCL