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

imgcodecs: fix pointer overflow in 16u icvCvt helpers

The 16-bit icvCvt_* helpers in utils.cpp move the row pointer with
step/sizeof(x[0]) - size.width*N. sizeof is unsigned, so this is done in
unsigned math. When the caller passes step == 0, it underflows to a huge
value and the pointer goes out of range, which is undefined behavior.

The TIFF decoder hits this: it walks rows itself and calls these helpers
with step = 0, so reading a 16-bit 4-channel TIFF triggers the bug.

Cast sizeof(...) to int so the math is signed. Same result for normal
calls, no overflow when the step is 0.

Added Imgcodecs_Tiff.regression_29615_16UC4 which round-trips a CV_16UC4
TIFF in memory.

This is the 4.x version of the fix, requested in
https://github.com/opencv/opencv/pull/29620 (issue #29615).
This commit is contained in:
Arnesh Banerjee
2026-07-28 16:19:17 +05:30
parent 70fc346580
commit b7860407f0
2 changed files with 28 additions and 6 deletions
+6 -6
View File
@@ -169,13 +169,13 @@ void icvCvt_Gray2BGR_16u_C1C3R( const ushort* gray, int gray_step,
ushort* bgr, int bgr_step, Size size )
{
int i;
for( ; size.height--; gray += gray_step/sizeof(gray[0]) )
for( ; size.height--; gray += gray_step/(int)sizeof(gray[0]) )
{
for( i = 0; i < size.width; i++, bgr += 3 )
{
bgr[0] = bgr[1] = bgr[2] = gray[i];
}
bgr += bgr_step/sizeof(bgr[0]) - size.width*3;
bgr += bgr_step/(int)sizeof(bgr[0]) - size.width*3;
}
}
@@ -214,8 +214,8 @@ void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step,
bgr[0] = t0; bgr[1] = t1;
t0 = bgra[swap_rb^2]; bgr[2] = t0;
}
bgr += bgr_step/sizeof(bgr[0]) - size.width*3;
bgra += bgra_step/sizeof(bgra[0]) - size.width*4;
bgr += bgr_step/(int)sizeof(bgr[0]) - size.width*3;
bgra += bgra_step/(int)sizeof(bgra[0]) - size.width*4;
}
}
@@ -252,8 +252,8 @@ void icvCvt_BGRA2RGBA_16u_C4R( const ushort* bgra, int bgra_step,
rgba[0] = t2; rgba[1] = t1;
rgba[2] = t0; rgba[3] = t3;
}
bgra += bgra_step/sizeof(bgra[0]) - size.width*4;
rgba += rgba_step/sizeof(rgba[0]) - size.width*4;
bgra += bgra_step/(int)sizeof(bgra[0]) - size.width*4;
rgba += rgba_step/(int)sizeof(rgba[0]) - size.width*4;
}
}
+22
View File
@@ -689,6 +689,28 @@ TEST(Imgcodecs_Tiff, readWrite_unsigned)
EXPECT_EQ(0, remove(filenameOutput.c_str()));
}
// See https://github.com/opencv/opencv/issues/29615
// Decoding a 16-bit 4-channel TIFF used to form an out of range pointer in
// icvCvt_BGRA2RGBA_16u_C4R because the byte step was divided by an unsigned
// sizeof and then had size.width*4 subtracted, which wraps around for a zero
// step. This just checks that a 16UC4 TIFF round-trips correctly; the value is
// mainly that the sanitizer builds no longer report the pointer overflow.
TEST(Imgcodecs_Tiff, regression_29615_16UC4)
{
Mat img(4, 3, CV_16UC4);
randu(img, Scalar::all(0), Scalar::all(65535));
vector<uchar> buf;
ASSERT_NO_THROW(ASSERT_TRUE(imencode(".tiff", img, buf)));
Mat decoded;
ASSERT_NO_THROW(decoded = imdecode(buf, IMREAD_UNCHANGED));
ASSERT_FALSE(decoded.empty());
ASSERT_EQ(CV_16UC4, decoded.type());
ASSERT_EQ(img.size(), decoded.size());
EXPECT_EQ(0, cvtest::norm(img, decoded, NORM_INF));
}
TEST(Imgcodecs_Tiff, readWrite_32FC1)
{
const string root = cvtest::TS::ptr()->get_data_path();