ARGBScale: Fix signed integer overflow when scaling large heights

In ScaleARGBBilinearDown and ScaleARGBBilinearUp, adding dy to y can
overflow signed 32-bit int when scaling down large height images (e.g.,
src_height = 32768 down to 2). Check if max_y - dy < y before adding dy
to y to prevent signed integer overflow.

R=wtc

Fixed: 540206290, 539110937
Test: LibYUVScaleTest.ARGBScale_LargeHeightOverflow
Change-Id: If353d002714481ce0018653798047066663f1fb9
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8175358
Reviewed-by: Frank Barchard <fbarchard@google.com>
Reviewed-by: Wan-Teh Chang <wtc@google.com>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
This commit is contained in:
Dale Curtis 2026-08-12 20:02:49 +00:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 79d22698bc
commit 2e58f4b971
3 changed files with 86 additions and 45 deletions

View File

@ -404,25 +404,23 @@ static int ScaleARGBBilinearDown(int src_width,
if (!row)
return 1;
const int max_y = (src_height - 1) << 16;
if (y > max_y) {
y = max_y;
}
const int64_t max_y = (int64_t)(src_height - 1) << 16;
int64_t y64 = y;
for (j = 0; j < dst_height; ++j) {
int yi = y >> 16;
if (y64 > max_y) {
y64 = max_y;
}
int yi = (int)(y64 >> 16);
const uint8_t* src = src_argb + yi * src_stride;
if (filtering == kFilterLinear) {
ScaleARGBFilterCols(dst_argb, src, dst_width, x, dx);
} else {
int yf = (y >> 8) & 255;
int yf = (int)((y64 >> 8) & 255);
InterpolateRow(row, src, src_stride, clip_src_width, yf);
ScaleARGBFilterCols(dst_argb, row, dst_width, x, dx);
}
dst_argb += dst_stride;
y += dy;
if (y > max_y) {
y = max_y;
}
y64 += dy;
}
free_aligned_buffer_64(row);
}
@ -450,7 +448,7 @@ static int ScaleARGBBilinearUp(int src_width,
void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
int dst_width, int x, int dx) =
filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
const int max_y = (src_height - 1) << 16;
const int64_t max_y = (int64_t)(src_height - 1) << 16;
#if defined(HAS_INTERPOLATEROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
InterpolateRow = InterpolateRow_Any_AVX2;
@ -550,12 +548,13 @@ static int ScaleARGBBilinearUp(int src_width,
#endif
}
if (y > max_y) {
y = max_y;
int64_t y64 = y;
if (y64 > max_y) {
y64 = max_y;
}
{
int yi = y >> 16;
int yi = (int)(y64 >> 16);
const uint8_t* src = src_argb + yi * src_stride;
// Allocate 2 rows of ARGB.
@ -578,31 +577,27 @@ static int ScaleARGBBilinearUp(int src_width,
}
for (j = 0; j < dst_height; ++j) {
yi = y >> 16;
if (y64 > max_y) {
y64 = max_y;
}
yi = (int)(y64 >> 16);
if (yi != lasty) {
if (y > max_y) {
y = max_y;
yi = y >> 16;
src = src_argb + yi * src_stride;
}
if (yi != lasty) {
ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y64 + 65536) < max_y) {
src += src_stride;
}
}
if (filtering == kFilterLinear) {
InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
} else {
int yf = (y >> 8) & 255;
int yf = (int)((y64 >> 8) & 255);
InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
}
dst_argb += dst_stride;
y += dy;
y64 += dy;
}
free_aligned_buffer_64(row);
}
@ -630,7 +625,7 @@ static void ScaleARGBSimple(int src_width,
void (*ScaleARGBCols)(uint8_t* dst_argb, const uint8_t* src_argb,
int dst_width, int x, int dx) =
(src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
(void)src_height;
const int64_t max_y = (int64_t)(src_height - 1) << 16;
#if defined(HAS_SCALEARGBCOLS_SSE2)
if (TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
ScaleARGBCols = ScaleARGBCols_SSE2;
@ -661,11 +656,15 @@ static void ScaleARGBSimple(int src_width,
#endif
}
int64_t y64 = y;
for (j = 0; j < dst_height; ++j) {
ScaleARGBCols(dst_argb, src_argb + (y >> 16) * src_stride, dst_width, x,
if (y64 > max_y) {
y64 = max_y;
}
ScaleARGBCols(dst_argb, src_argb + (y64 >> 16) * src_stride, dst_width, x,
dx);
dst_argb += dst_stride;
y += dy;
y64 += dy;
}
}

View File

@ -1474,7 +1474,9 @@ void ScalePlaneVertical(int src_height,
void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
ptrdiff_t src_stride, int dst_width,
int source_y_fraction) = InterpolateRow_C;
const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
const int64_t max_y =
(src_height > 1) ? (((int64_t)src_height - 1) << 16) - 1 : 0;
int64_t y64 = y;
int j;
assert(bpp >= 1 && bpp <= 4);
assert(src_height != 0);
@ -1524,15 +1526,15 @@ void ScalePlaneVertical(int src_height,
for (j = 0; j < dst_height; ++j) {
int yi;
int yf;
if (y > max_y) {
y = max_y;
if (y64 > max_y) {
y64 = max_y;
}
yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0;
yi = (int)(y64 >> 16);
yf = filtering ? (int)((y64 >> 8) & 255) : 0;
InterpolateRow(dst_argb, src_argb + yi * (ptrdiff_t)src_stride, src_stride,
dst_width_bytes, yf);
dst_argb += dst_stride;
y += dy;
y64 += dy;
}
}
@ -1553,7 +1555,9 @@ void ScalePlaneVertical_16(int src_height,
void (*InterpolateRow)(uint16_t* dst_argb, const uint16_t* src_argb,
ptrdiff_t src_stride, int dst_width,
int source_y_fraction) = InterpolateRow_16_C;
const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
const int64_t max_y =
(src_height > 1) ? (((int64_t)src_height - 1) << 16) - 1 : 0;
int64_t y64 = y;
int j;
assert(wpp >= 1 && wpp <= 2);
assert(src_height != 0);
@ -1592,15 +1596,15 @@ void ScalePlaneVertical_16(int src_height,
for (j = 0; j < dst_height; ++j) {
int yi;
int yf;
if (y > max_y) {
y = max_y;
if (y64 > max_y) {
y64 = max_y;
}
yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0;
yi = (int)(y64 >> 16);
yf = filtering ? (int)((y64 >> 8) & 255) : 0;
InterpolateRow(dst_argb, src_argb + yi * (ptrdiff_t)src_stride, src_stride,
dst_width_words, yf);
dst_argb += dst_stride;
y += dy;
y64 += dy;
}
}

View File

@ -597,4 +597,42 @@ TEST_F(LibYUVScaleTest, ARGBTest4x) {
free_aligned_buffer_page_end(orig_pixels);
}
TEST_F(LibYUVScaleTest, ARGBScale_LargeHeightOverflow) {
const int src_w = 4;
const int src_h = 32767;
align_buffer_page_end(orig_pixels, src_w * src_h * 4);
align_buffer_page_end(dest_pixels, src_w * 2 * 4);
memset(orig_pixels, 128, src_w * src_h * 4);
// Test both vertical-only scaling (dst_w == src_w) and 2D scaling (dst_w != src_w).
for (int dst_w : {4, 2}) {
int res = ARGBScale(orig_pixels, src_w * 4, src_w, src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterBilinear);
EXPECT_EQ(0, res);
res = ARGBScale(orig_pixels, src_w * 4, src_w, -src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterBilinear);
EXPECT_EQ(0, res);
res = ARGBScale(orig_pixels, src_w * 4, src_w, src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterLinear);
EXPECT_EQ(0, res);
res = ARGBScale(orig_pixels, src_w * 4, src_w, -src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterLinear);
EXPECT_EQ(0, res);
res = ARGBScale(orig_pixels, src_w * 4, src_w, src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterNone);
EXPECT_EQ(0, res);
res = ARGBScale(orig_pixels, src_w * 4, src_w, -src_h, dest_pixels,
dst_w * 4, dst_w, 2, kFilterNone);
EXPECT_EQ(0, res);
}
free_aligned_buffer_page_end(dest_pixels);
free_aligned_buffer_page_end(orig_pixels);
}
} // namespace libyuv