1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

imgcodecs: fix pointer overflow in 16u icvCvt helpers

The 16-bit icvCvt_* helpers in utils.cpp advance the row pointer with
step/sizeof(x[0]) - size.width*N at the end of each row. sizeof is size_t
(unsigned), so the whole subtraction is done in unsigned math and wraps to
a huge offset when the caller passes step == 0.

The TIFF tile decoder does exactly that: it walks rows itself and calls
these helpers with step = 0 and Size(width, 1), so decoding a 16-bit
4-channel TIFF forms an out of range pointer. UBSan reports it as an
unsigned offset overflow (issue #29615).

Cast sizeof(...) to int so the step math is signed. The result is the same
for valid multi-row calls and no longer overflows when step is 0.

Added Imgcodecs_Tiff.regression_29615_16UC4 which round-trips a CV_16UC4
TIFF in memory.
This commit is contained in:
Arnesh Banerjee
2026-07-27 19:32:12 +05:30
parent 53d308c2a5
commit 7a25b1986d
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();