[libyuv] Fix security vulnerabilities in ScalePlane and ARGBAffineRow_C

This CL addresses two security findings related to integer overflows:

1. Input validation in ScalePlane, ScalePlane_16, and ScalePlane_12:
   Added checks to reject invalid dimensions (e.g. width <= 0, height
   == 0) and dimensions larger than 32768 (or smaller than -32768 for
   height). This prevents FixedDiv signed integer overflows that can
   lead to division by zero/overflow crashes (SIGFPE on x86) or
   incorrect step calculations.

2. Stride overflow in ARGBAffineRow_C:
   Casted pointer arithmetic operands to ptrdiff_t before multiplication
   (y * stride and x * 4) to ensure 64-bit calculations, preventing
   signed 32-bit integer overflow when calculating source pixel offsets.

Added unit tests to verify the input validation in ScalePlane functions.

Test: libyuv_unittest --gtest_filter=*InvalidInputs*
Test: libyuv_unittest --gtest_filter=*Scale*
Test: libyuv_unittest --gtest_filter=*TestAffine*
Bug: None

TAG=agy
CONV=0e990960-611b-4f38-94ec-24e79b66242e
R=wtc@google.com

Change-Id: I252af47a98e45dff8bb5f06308c3739c6eead741
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/7886217
Reviewed-by: Wan-Teh Chang <wtc@google.com>
Commit-Queue: Frank Barchard <fbarchard@google.com>
This commit is contained in:
Frank Barchard 2026-05-29 17:52:58 -07:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent c98edcc8dc
commit ef08f21f6d
3 changed files with 75 additions and 1 deletions

View File

@ -3663,7 +3663,8 @@ void ARGBAffineRow_C(const uint8_t* src_argb,
int x = (int)(uv[0]);
int y = (int)(uv[1]);
*(uint32_t*)(dst_argb) =
*(const uint32_t*)(src_argb + y * src_argb_stride + x * 4);
*(const uint32_t*)(src_argb + (ptrdiff_t)y * src_argb_stride +
(ptrdiff_t)x * 4);
dst_argb += 4;
uv[0] += uv_dudv[2];
uv[1] += uv_dudv[3];

View File

@ -1947,6 +1947,14 @@ int ScalePlane(const uint8_t* src,
int dst_width,
int dst_height,
enum FilterMode filtering) {
// Reject dimensions larger than 32768 (or smaller than -32768 for height).
// This prevents FixedDiv signed integer overflows that can lead to division
// by zero/overflow crashes (SIGFPE on x86) or incorrect step calculations.
if (!src || src_width <= 0 || src_height == 0 ||
src_width > 32768 || src_height < -32768 || src_height > 32768 ||
!dst || dst_width <= 0 || dst_height <= 0) {
return -1;
}
// Simplify filtering when possible.
filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
filtering);
@ -2047,6 +2055,14 @@ int ScalePlane_16(const uint16_t* src,
int dst_width,
int dst_height,
enum FilterMode filtering) {
// Reject dimensions larger than 32768 (or smaller than -32768 for height).
// This prevents FixedDiv signed integer overflows that can lead to division
// by zero/overflow crashes (SIGFPE on x86) or incorrect step calculations.
if (!src || src_width <= 0 || src_height == 0 ||
src_width > 32768 || src_height < -32768 || src_height > 32768 ||
!dst || dst_width <= 0 || dst_height <= 0) {
return -1;
}
// Simplify filtering when possible.
filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
filtering);
@ -2151,6 +2167,14 @@ int ScalePlane_12(const uint16_t* src,
int dst_width,
int dst_height,
enum FilterMode filtering) {
// Reject dimensions larger than 32768 (or smaller than -32768 for height).
// This prevents FixedDiv signed integer overflows that can lead to division
// by zero/overflow crashes (SIGFPE on x86) or incorrect step calculations.
if (!src || src_width <= 0 || src_height == 0 ||
src_width > 32768 || src_height < -32768 || src_height > 32768 ||
!dst || dst_width <= 0 || dst_height <= 0) {
return -1;
}
// Simplify filtering when possible.
filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
filtering);

View File

@ -636,4 +636,53 @@ TEST_F(LibYUVScaleTest, ScalePlaneVertical_IntStrideOverflow) {
delete[] dst;
}
TEST_F(LibYUVScaleTest, ScalePlane_InvalidInputs) {
uint8_t src[16] = {0};
uint8_t dst[16] = {0};
// NULL src/dst
EXPECT_EQ(-1, ScalePlane(nullptr, 4, 4, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 4, nullptr, 4, 4, 4, kFilterNone));
// Width/height <= 0 (except src_height which can be negative but not 0)
EXPECT_EQ(-1, ScalePlane(src, 4, 0, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, -1, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 0, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 4, dst, 4, 0, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 4, dst, 4, -1, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 4, dst, 4, 4, 0, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 4, dst, 4, 4, -1, kFilterNone));
// Width/height too large (> 32768)
EXPECT_EQ(-1, ScalePlane(src, 4, 32769, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, 32769, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane(src, 4, 4, -32769, dst, 4, 4, 4, kFilterNone));
// Valid edge cases
EXPECT_EQ(0, ScalePlane(src, 4, 1, 1, dst, 4, 1, 1, kFilterNone));
EXPECT_EQ(0, ScalePlane(src, 4, 1, -1, dst, 4, 1, 1, kFilterNone));
}
TEST_F(LibYUVScaleTest, ScalePlane_16_InvalidInputs) {
uint16_t src[16] = {0};
uint16_t dst[16] = {0};
EXPECT_EQ(-1, ScalePlane_16(nullptr, 4, 4, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_16(src, 4, 4, 4, nullptr, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_16(src, 4, 0, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_16(src, 4, 32769, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_16(src, 4, 4, -32769, dst, 4, 4, 4, kFilterNone));
}
TEST_F(LibYUVScaleTest, ScalePlane_12_InvalidInputs) {
uint16_t src[16] = {0};
uint16_t dst[16] = {0};
EXPECT_EQ(-1, ScalePlane_12(nullptr, 4, 4, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_12(src, 4, 4, 4, nullptr, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_12(src, 4, 0, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_12(src, 4, 32769, 4, dst, 4, 4, 4, kFilterNone));
EXPECT_EQ(-1, ScalePlane_12(src, 4, 4, -32769, dst, 4, 4, 4, kFilterNone));
}
} // namespace libyuv