scale_argb: Harden entry points against large dims

Reject dimensions that overflow the internal fixed-point (dimension << 16)
and FixedDiv arithmetic in scale_argb entry points:

- ARGBScale and ARGBScaleClip: add the missing src_width and src_height
  bounds so both are rejected in each direction before reaching ScaleARGB.

Add a unit test covering the ARGBScale / ARGBScaleClip paths.

Test: libyuv_unittest --gtest_filter=*.ARGBScaleDimensionOverflow
Co-authored-by: Timothy Nikkel <tnikkel@mozilla.com>
Bug: chromium:531172025
Change-Id: I49577c675725f7d86895ade7eaead826373bd197
This commit is contained in:
Wan-Teh Chang 2026-07-29 16:05:27 -07:00
parent b56492e2df
commit 984a8d0a74
2 changed files with 43 additions and 8 deletions

View File

@ -697,14 +697,14 @@ static int ScaleARGB(const uint8_t* src,
if (clip_x) {
int64_t clipf = (int64_t)(clip_x)*dx;
x += (clipf & 0xffff);
src += (clipf >> 16) * 4;
dst += clip_x * 4;
src += (ptrdiff_t)(clipf >> 16) * 4;
dst += (ptrdiff_t)clip_x * 4;
}
if (clip_y) {
int64_t clipf = (int64_t)(clip_y)*dy;
y += (clipf & 0xffff);
src += (clipf >> 16) * (ptrdiff_t)src_stride;
dst += clip_y * (ptrdiff_t)dst_stride;
src += (ptrdiff_t)(clipf >> 16) * src_stride;
dst += (ptrdiff_t)clip_y * dst_stride;
}
// Special case for integer step values.
@ -779,7 +779,8 @@ int ARGBScaleClip(const uint8_t* src_argb,
int clip_width,
int clip_height,
enum FilterMode filtering) {
if (!src_argb || src_width == 0 || src_height == 0 || src_height == INT_MIN ||
if (!src_argb || src_width == 0 || src_width > 32768 || src_width < -32768 ||
src_height == 0 || src_height > 32768 || src_height < -32768 ||
!dst_argb || dst_width <= 0 || dst_height <= 0 || clip_x < 0 ||
clip_y < 0 || clip_width > 32768 || clip_height > 32768 ||
(clip_x + clip_width) > dst_width ||
@ -802,9 +803,9 @@ int ARGBScale(const uint8_t* src_argb,
int dst_width,
int dst_height,
enum FilterMode filtering) {
if (!src_argb || src_width == 0 || src_height == 0 || src_height == INT_MIN ||
src_width > 32768 || src_height > 32768 || !dst_argb || dst_width <= 0 ||
dst_height <= 0) {
if (!src_argb || src_width == 0 || src_width > 32768 || src_width < -32768 ||
src_height == 0 || src_height > 32768 || src_height < -32768 ||
!dst_argb || dst_width <= 0 || dst_height <= 0) {
return -1;
}
return ScaleARGB(src_argb, src_stride_argb, src_width, src_height, dst_argb,

View File

@ -9,6 +9,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../unit_test/unit_test.h"
@ -597,4 +598,37 @@ TEST_F(LibYUVScaleTest, ARGBTest4x) {
free_aligned_buffer_page_end(orig_pixels);
}
// ARGBScale and ARGBScaleClip reach ScaleARGB, which accepts negative width
// (horizontal mirror) and height (flip) and takes Abs()/flips to positive
// before forming (src_height - 1) << 16 and the step
// dx = FixedDiv(Abs(src_width), dst_width) -- both must fit in a signed 16.16
// value. Verify oversize width and height are rejected in both directions.
TEST_F(LibYUVScaleTest, ARGBScaleDimensionOverflow) {
int width = 16;
int height = 40000;
align_buffer_page_end(src, width * height);
align_buffer_page_end(dst, width * height);
memset(src, 0, width * height);
EXPECT_EQ(-1, ARGBScale(src, 16, 1, 32769, dst, 16, 1, 1, kFilterBilinear));
EXPECT_EQ(-1, ARGBScale(src, 16, 1, -40000, dst, 16, 1, 1, kFilterBilinear));
EXPECT_EQ(-1, ARGBScale(src, 16, 1, 40000, dst, 16, 1, 1, kFilterBilinear));
EXPECT_EQ(-1,
ARGBScale(src, 40000, 40000, 1, dst, 16, 1, 1, kFilterBilinear));
EXPECT_EQ(-1,
ARGBScale(src, 40000, -40000, 1, dst, 16, 1, 1, kFilterBilinear));
EXPECT_EQ(-1, ARGBScaleClip(src, 16, 1, 40000, dst, 16, 1, 1, 0, 0, 1, 1,
kFilterBilinear));
EXPECT_EQ(-1, ARGBScaleClip(src, 16, 1, -40000, dst, 16, 1, 1, 0, 0, 1, 1,
kFilterBilinear));
EXPECT_EQ(-1, ARGBScaleClip(src, 40000, 40000, 1, dst, 16, 1, 1, 0, 0, 1, 1,
kFilterBilinear));
EXPECT_EQ(-1, ARGBScaleClip(src, 40000, -40000, 1, dst, 16, 1, 1, 0, 0, 1, 1,
kFilterBilinear));
free_aligned_buffer_page_end(src);
free_aligned_buffer_page_end(dst);
}
} // namespace libyuv