Fix yi * src_stride overflow in ScalePlaneVertical

Fix int overflow of yi * src_stride overflow in ScalePlaneVertical(),
ScalePlaneVertical_16(), and ScalePlaneVertical_16To8() by casting the
operand src_stride to ptrdiff_t.

Adapted from the patches by Victor Miura <vmiura@google.com>.

Bug: 505814332
Change-Id: I4a4751041a213f7208b01eb18c43c9e196a36261
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/7796558
Commit-Queue: Wan-Teh Chang <wtc@google.com>
Reviewed-by: Frank Barchard <fbarchard@google.com>
This commit is contained in:
Wan-Teh Chang 2026-04-28 11:42:50 -07:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 2895faed32
commit a7849e8a5e
4 changed files with 78 additions and 6 deletions

View File

@ -1,6 +1,6 @@
Name: libyuv Name: libyuv
URL: https://chromium.googlesource.com/libyuv/libyuv/ URL: https://chromium.googlesource.com/libyuv/libyuv/
Version: 1932 Version: 1933
Revision: DEPS Revision: DEPS
License: BSD-3-Clause License: BSD-3-Clause
License File: LICENSE License File: LICENSE

View File

@ -11,6 +11,6 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_ #ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_ #define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 1932 #define LIBYUV_VERSION 1933
#endif // INCLUDE_LIBYUV_VERSION_H_ #endif // INCLUDE_LIBYUV_VERSION_H_

View File

@ -1687,7 +1687,7 @@ void ScalePlaneVertical(int src_height,
} }
yi = y >> 16; yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0; yf = filtering ? ((y >> 8) & 255) : 0;
InterpolateRow(dst_argb, src_argb + yi * src_stride, src_stride, InterpolateRow(dst_argb, src_argb + yi * (ptrdiff_t)src_stride, src_stride,
dst_width_bytes, yf); dst_width_bytes, yf);
dst_argb += dst_stride; dst_argb += dst_stride;
y += dy; y += dy;
@ -1763,7 +1763,7 @@ void ScalePlaneVertical_16(int src_height,
} }
yi = y >> 16; yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0; yf = filtering ? ((y >> 8) & 255) : 0;
InterpolateRow(dst_argb, src_argb + yi * src_stride, src_stride, InterpolateRow(dst_argb, src_argb + yi * (ptrdiff_t)src_stride, src_stride,
dst_width_words, yf); dst_width_words, yf);
dst_argb += dst_stride; dst_argb += dst_stride;
y += dy; y += dy;
@ -1832,8 +1832,8 @@ void ScalePlaneVertical_16To8(int src_height,
} }
yi = y >> 16; yi = y >> 16;
yf = filtering ? ((y >> 8) & 255) : 0; yf = filtering ? ((y >> 8) & 255) : 0;
InterpolateRow_16To8(dst_argb, src_argb + yi * src_stride, src_stride, InterpolateRow_16To8(dst_argb, src_argb + yi * (ptrdiff_t)src_stride,
scale, dst_width_words, yf); src_stride, scale, dst_width_words, yf);
dst_argb += dst_stride; dst_argb += dst_stride;
y += dy; y += dy;
} }

View File

@ -8,9 +8,14 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include <limits.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <new>
#include "../unit_test/unit_test.h" #include "../unit_test/unit_test.h"
#include "libyuv/cpu_id.h" #include "libyuv/cpu_id.h"
#include "libyuv/scale.h" #include "libyuv/scale.h"
@ -462,4 +467,71 @@ TEST_F(LibYUVScaleTest, PlaneTest1_16_Box) {
free_aligned_buffer_page_end(dst_pixels_alloc); free_aligned_buffer_page_end(dst_pixels_alloc);
free_aligned_buffer_page_end(orig_pixels_alloc); free_aligned_buffer_page_end(orig_pixels_alloc);
} }
// POC: int * int overflow in ScalePlaneVertical (scale_common.cc).
//
// `yi * src_stride` is evaluated as int * int. When the product exceeds
// INT_MAX it wraps negative and InterpolateRow reads from BEFORE the
// source allocation.
//
// Parameters:
// - dst_width == src_width
// -> ScalePlane dispatches to ScalePlaneVertical
// - src_height == 5, dst_height == 1
// -> single iteration with yi == 2
// - src_stride == 0x7FFFFFF8
// -> 2 * 0x7FFFFFF8 == 0xFFFFFFF0 == -16 (int)
//
// The source buffer is sized so that the *correct* 64-bit offset
// (2 * 0x7FFFFFF8 == 4294967280) plus kWidth bytes is in-bounds. With the
// bug, the 32-bit product is -16 and ASAN reports a heap-buffer-overflow
// READ "16 bytes before" the allocation.
TEST_F(LibYUVScaleTest, ScalePlaneVertical_IntStrideOverflow) {
const int kWidth = 16;
const int kSrcHeight = 5;
const int kDstHeight = 1;
const int kStride = 0x7FFFFFF8; // 2147483640
// src_size is big enough for the only row this call legitimately touches
// (yi == 2) when computed in 64-bit: 2 * stride + width = 4 GiB.
size_t src_size = kStride;
if (src_size > SIZE_MAX / 2) {
GTEST_SKIP() << "could not represent allocation size in size_t";
}
src_size *= 2;
if (src_size > SIZE_MAX - kWidth) {
GTEST_SKIP() << "could not represent allocation size in size_t";
}
src_size += kWidth;
#if defined(__aarch64__)
// Infer malloc can accept a large size for cpu with dot product (a76/a55)
int has_large_malloc = TestCpuFlag(kCpuHasNeonDotProd);
#else
int has_large_malloc = 1;
#endif
if (!has_large_malloc) {
GTEST_SKIP() << "large allocation may assert for " << src_size << " bytes";
}
uint8_t* src = new (std::nothrow) uint8_t[src_size];
if (!src) {
GTEST_SKIP() << "could not allocate " << src_size << " bytes";
}
uint8_t* dst = new uint8_t[kWidth];
memset(dst, 0, kWidth);
// Force the scalar path so the crash site is deterministic
// (InterpolateRow_C -> memcpy when yf == 0).
MaskCpuFlags(disable_cpu_flags_);
int r = ScalePlane(src, kStride, kWidth, kSrcHeight, dst, kWidth, kWidth,
kDstHeight, kFilterNone);
// Not reached under ASAN.
EXPECT_EQ(0, r);
delete[] src;
delete[] dst;
}
} // namespace libyuv } // namespace libyuv