1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #29093 from varun-jaiswal17:dnn-overflow-large-image

fix int32 overflow in shape_utils::total() for large tensors #29093

Fixes https://github.com/opencv/opencv/issues/24914

### Problem

When running inference with a ConvTranspose (deconvolution) layer on large inputs
(e.g. 4864×4864 with 30 channels), the DNN module crashes with:

OpenCV net_impl.cpp: error:

(expected: 'total(ints[i]) > 0'), where
'total(ints[i])' is -1455947776
must be greater than
'0' is 0

The root cause is `shape_utils::total()` which returns `int` (32-bit signed).

`ENGINE_CLASSIC` catches this via `CV_CheckGT` and throws.
`ENGINE_NEW` was silently bypassing the check — the overflow in `total()` itself
was never addressed.

### Changes

**`modules/dnn/include/opencv2/dnn/shape_utils.hpp`** — root fix
- Changed return type of both `total()` overloads from `int` to `size_t`
- Changed accumulator from `int elems = 1` to `size_t elems = 1`

**`modules/dnn/src/net_impl.cpp`**
- Updated `CV_CheckGT(total(...), 0)` to `CV_CheckGT(total(...), (size_t)0)`
  to match the new return type

**`modules/dnn/src/net_impl2.cpp`**
- Added the same `CV_CheckGT` shape validation that `ENGINE_CLASSIC` has in
  `net_impl.cpp:1333-1337` — `ENGINE_NEW` was missing this check entirely

**`modules/dnn/src/legacy_backend.hpp`**
- Removed the now-incorrect `(int)` cast in `CV_CheckEQ` — both sides are
  now `size_t`

### Test

Added `Net.ShapeUtils_total_no_int32_overflow` in `modules/dnn/test/test_misc.cpp`:
- The shape [1920 × 1,478,656] is the exact im2col buffer from the bug report.
EXPECT_EQ verifies total() returns the correct size_t value 2,839,019,520.
EXPECT_LT documents that casting it to int wraps to -1,455,947,776 — the
value that caused the original crash.



### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake

<!-- Note!!! If you are an automated agent, we have a special process for you: add 🤖🤖🤖 to the end of the PR title. -->
This commit is contained in:
Varun Jaiswal
2026-05-22 22:26:51 +05:30
committed by GitHub
parent 2c7e9ebbaf
commit 104d987ca2
8 changed files with 31 additions and 16 deletions
@@ -152,7 +152,7 @@ static inline MatShape shape(int a0, int a1=-1, int a2=-1, int a3=-1)
return shape(shape_, dims);
}
static inline int total(const MatShape& shape, int start = -1, int end = -1)
static inline size_t total(const MatShape& shape, int start = -1, int end = -1)
{
//if (shape.empty())
// return 0;
@@ -166,16 +166,16 @@ static inline int total(const MatShape& shape, int start = -1, int end = -1)
CV_CheckLE(start, end, "");
CV_CheckLE(end, dims, "");
int elems = 1;
size_t elems = 1;
for (int i = start; i < end; i++)
{
elems *= shape[i];
elems *= (size_t)shape[i];
}
return elems;
}
// TODO: rename to countDimsElements()
static inline int total(const Mat& mat, int start = -1, int end = -1)
static inline size_t total(const Mat& mat, int start = -1, int end = -1)
{
if (mat.empty())
return 0;
@@ -189,10 +189,10 @@ static inline int total(const Mat& mat, int start = -1, int end = -1)
CV_CheckLE(start, end, "");
CV_CheckLE(end, dims, "");
int elems = 1;
size_t elems = 1;
for (int i = start; i < end; i++)
{
elems *= mat.size[i];
elems *= (size_t)mat.size[i];
}
return elems;
}
+2 -2
View File
@@ -244,9 +244,9 @@ public:
return false;
int bottom_concat_axis;
int concat_size = total(shape(inputs[0]), cAxis + 1);
int concat_size = (int)total(shape(inputs[0]), cAxis + 1);
int top_concat_axis = outputs[0].size[cAxis];
int num_concats = total(shape(inputs[0]), 0, cAxis);
int num_concats = (int)total(shape(inputs[0]), 0, cAxis);
int offset_concat_axis = 0;
UMat& outMat = outputs[0];
String matType = matTypeToOclType(inputs[0].type());
@@ -225,7 +225,7 @@ public:
}
getMemoryShapes(inpShapes, 1, outShapes, internals);
Mat zeros = Mat::zeros(1, total(outShapes[0]), CV_32F);
Mat zeros = Mat::zeros(1, (int)total(outShapes[0]), CV_32F);
auto zeroInp = std::make_shared<ov::op::v0::Constant>(ov::element::f32, ov::Shape{zeros.total()}, zeros.data);
int newShape = -1;
+1 -1
View File
@@ -419,7 +419,7 @@ public:
// set the variance.
{
ocl::Kernel kernel("set_variance", ocl::dnn::prior_box_oclsrc, opts);
int offset = total(shape(outputs[0]), 2);
int offset = (int)total(shape(outputs[0]), 2);
size_t nthreads = _layerHeight * _layerWidth * _numPriors;
kernel.set(0, (int)nthreads);
kernel.set(1, (int)offset);
+2 -2
View File
@@ -190,7 +190,7 @@ public:
std::map<LayerPin, Mat>::const_iterator hostIt;
std::map<LayerPin, int>::const_iterator refIt;
const int targetTotal = total(shape);
const int targetTotal = (int)total(shape);
size_t bestBlobTotal = INT_MAX;
for (hostIt = memHosts.begin(); hostIt != memHosts.end(); ++hostIt)
@@ -299,7 +299,7 @@ public:
LayerPin blobPin(ld.id, index);
if (index < outShapes.size() && inPlace)
{
CV_CheckEQ((int)ld.inputBlobs[0]->total(), total(shapes[index]), "");
CV_CheckEQ(ld.inputBlobs[0]->total(), total(shapes[index]), "");
CV_CheckTypeEQ(ld.inputBlobs[0]->type(), types[index], "blob can't be reused if it has different type");
ld.outputBlobs[index] = ld.inputBlobs[0]->reshape(1, shapes[index]);
reuse(ld.inputBlobsId[0], blobPin);
+4 -4
View File
@@ -1330,11 +1330,11 @@ void Net::Impl::getLayerShapesRecursively(int id, LayersShapesMap& inOutShapes)
try
{
for (int i = 0; i < ints.size(); i++)
CV_CheckGT(total(ints[i]), 0, "");
for (int i = 0; i < (int)ints.size(); i++)
CV_CheckGT(total(ints[i]), (size_t)0, "");
for (int i = 0; i < os.size(); i++)
CV_CheckGT(total(os[i]), 0, "");
for (int i = 0; i < (int)os.size(); i++)
CV_CheckGT(total(os[i]), (size_t)0, "");
}
catch (const cv::Exception& e)
{
+6
View File
@@ -598,6 +598,9 @@ void Net::Impl::allocateLayerOutputs(
CV_Assert(tempShapes.size() == tempTypes.size());
CV_Assert(outShapes.size() == outTypes.size());
CV_Assert(outShapes.size() == noutputs);
for (int i = 0; i < (int)tempShapes.size(); i++)
CV_CheckGT(total(tempShapes[i]), (size_t)0, "");
outputs.assign(noutputs, Mat());
outOrigData.resize(noutputs);
for (size_t i = 0; i < noutputs; i++) {
@@ -1628,6 +1631,9 @@ bool Net::Impl::tryInferGraphShapes(const Ptr<Graph>& graph,
layer->getTypes(inpTypes, noutputs, (int)tempShapes.size(), outTypes, tempTypes);
CV_Assert((int)outTypes.size() == noutputs);
for (int i = 0; i < (int)tempShapes.size(); i++)
CV_CheckGT(total(tempShapes[i]), (size_t)0, "");
for (int i = 0; i < noutputs; i++) {
Arg out = outputs[i];
if (out.idx == 0)
+9
View File
@@ -10,6 +10,7 @@
#include <opencv2/core/ocl.hpp>
#include <opencv2/core/opencl/ocl_defs.hpp>
#include <opencv2/dnn/layer.details.hpp> // CV_DNN_REGISTER_LAYER_CLASS
#include <opencv2/dnn/shape_utils.hpp>
namespace opencv_test { namespace {
@@ -1071,4 +1072,12 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_two_inputs, Combine(
dnnBackendsAndTargets()
));
TEST(Net, ShapeUtils_total_no_int32_overflow)
{
cv::MatShape shape = cv::dnn::shape(1920, 1478656);
size_t t = cv::dnn::total(shape);
EXPECT_EQ(t, 2839019520llu);
}
}} // namespace