Commit 7910e0de authored by Eric Karl's avatar Eric Karl Committed by Commit Bot

Fix false-negative in ResourceSizes::VerifyFitsInBytesInternal

VerifyFitsInBytesInternal would return false if a size/format combo
had a size in *bits* that overflowed the requested type, even if the
final size in *bytes* fit. Updated the logic to handle this.

Additionally, Maybe(Width|Size)InBytes would crash if width overflowed,
also fixed and test added.

Bug: 1002855
Change-Id: I1fde167f17fdddfbcf4f9269e8a681a062d9ff05
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1803831
Commit-Queue: Eric Karl <ericrk@chromium.org>
Auto-Submit: Eric Karl <ericrk@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698592}
parent 18f2fb89
...@@ -71,18 +71,37 @@ class VIZ_RESOURCE_FORMAT_EXPORT ResourceSizes { ...@@ -71,18 +71,37 @@ class VIZ_RESOURCE_FORMAT_EXPORT ResourceSizes {
static inline void VerifyType(); static inline void VerifyType();
template <typename T> template <typename T>
static bool VerifyFitsInBytesInternal(int width, static bool VerifyWidthInBytesInternal(int width,
int height, ResourceFormat format,
bool aligned);
template <typename T>
static bool VerifySizeInBytesInternal(const gfx::Size& size,
ResourceFormat format, ResourceFormat format,
bool verify_size,
bool aligned); bool aligned);
template <typename T> template <typename T>
static T BytesInternal(int width, static bool MaybeWidthInBytesInternal(int width,
int height, ResourceFormat format,
ResourceFormat format, bool aligned,
bool verify_size, T* bytes);
bool aligned);
template <typename T>
static bool MaybeSizeInBytesInternal(const gfx::Size& size,
ResourceFormat format,
bool aligned,
T* bytes);
template <typename T>
static T WidthInBytesInternal(int width, ResourceFormat format, bool aligned);
template <typename T>
static T SizeInBytesInternal(const gfx::Size& size,
ResourceFormat format,
bool aligned);
template <typename T>
static bool MaybeRound(base::CheckedNumeric<T>* value, T mul);
// Not instantiable. // Not instantiable.
ResourceSizes() = delete; ResourceSizes() = delete;
...@@ -93,7 +112,7 @@ bool ResourceSizes::VerifyWidthInBytes(int width, ResourceFormat format) { ...@@ -93,7 +112,7 @@ bool ResourceSizes::VerifyWidthInBytes(int width, ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
if (width <= 0) if (width <= 0)
return false; return false;
return VerifyFitsInBytesInternal<T>(width, 0, format, false, false); return VerifyWidthInBytesInternal<T>(width, format, false);
} }
template <typename T> template <typename T>
...@@ -102,8 +121,7 @@ bool ResourceSizes::VerifySizeInBytes(const gfx::Size& size, ...@@ -102,8 +121,7 @@ bool ResourceSizes::VerifySizeInBytes(const gfx::Size& size,
VerifyType<T>(); VerifyType<T>();
if (size.IsEmpty()) if (size.IsEmpty())
return false; return false;
return VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, return VerifySizeInBytesInternal<T>(size, format, false);
false);
} }
template <typename T> template <typename T>
...@@ -113,15 +131,7 @@ bool ResourceSizes::MaybeWidthInBytes(int width, ...@@ -113,15 +131,7 @@ bool ResourceSizes::MaybeWidthInBytes(int width,
VerifyType<T>(); VerifyType<T>();
if (width <= 0) if (width <= 0)
return false; return false;
base::CheckedNumeric<T> checked_value = BitsPerPixel(format); return MaybeWidthInBytesInternal<T>(width, format, false, bytes);
checked_value *= width;
checked_value =
cc::MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8);
checked_value /= 8;
if (!checked_value.IsValid())
return false;
*bytes = checked_value.ValueOrDie();
return true;
} }
template <typename T> template <typename T>
...@@ -131,29 +141,16 @@ bool ResourceSizes::MaybeSizeInBytes(const gfx::Size& size, ...@@ -131,29 +141,16 @@ bool ResourceSizes::MaybeSizeInBytes(const gfx::Size& size,
VerifyType<T>(); VerifyType<T>();
if (size.IsEmpty()) if (size.IsEmpty())
return false; return false;
base::CheckedNumeric<T> checked_value = BitsPerPixel(format); return MaybeSizeInBytesInternal<T>(size, format, false, bytes);
checked_value *= size.width();
checked_value =
cc::MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8);
checked_value /= 8;
checked_value *= size.height();
if (!checked_value.IsValid())
return false;
*bytes = checked_value.ValueOrDie();
return true;
} }
template <typename T> template <typename T>
T ResourceSizes::CheckedWidthInBytes(int width, ResourceFormat format) { T ResourceSizes::CheckedWidthInBytes(int width, ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
CHECK_GT(width, 0); CHECK_GT(width, 0);
DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); T bytes;
base::CheckedNumeric<T> checked_value = BitsPerPixel(format); CHECK(MaybeWidthInBytesInternal<T>(width, format, false, &bytes));
checked_value *= width; return bytes;
checked_value =
cc::MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8);
checked_value /= 8;
return checked_value.ValueOrDie();
} }
template <typename T> template <typename T>
...@@ -161,23 +158,17 @@ T ResourceSizes::CheckedSizeInBytes(const gfx::Size& size, ...@@ -161,23 +158,17 @@ T ResourceSizes::CheckedSizeInBytes(const gfx::Size& size,
ResourceFormat format) { ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
CHECK(!size.IsEmpty()); CHECK(!size.IsEmpty());
DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, T bytes;
false)); CHECK(MaybeSizeInBytesInternal<T>(size, format, false, &bytes));
base::CheckedNumeric<T> checked_value = BitsPerPixel(format); return bytes;
checked_value *= size.width();
checked_value =
cc::MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8);
checked_value /= 8;
checked_value *= size.height();
return checked_value.ValueOrDie();
} }
template <typename T> template <typename T>
T ResourceSizes::UncheckedWidthInBytes(int width, ResourceFormat format) { T ResourceSizes::UncheckedWidthInBytes(int width, ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
DCHECK_GT(width, 0); DCHECK_GT(width, 0);
DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); DCHECK(VerifyWidthInBytesInternal<T>(width, format, false));
return BytesInternal<T>(width, 0, format, false, false); return WidthInBytesInternal<T>(width, format, false);
} }
template <typename T> template <typename T>
...@@ -185,9 +176,8 @@ T ResourceSizes::UncheckedSizeInBytes(const gfx::Size& size, ...@@ -185,9 +176,8 @@ T ResourceSizes::UncheckedSizeInBytes(const gfx::Size& size,
ResourceFormat format) { ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
DCHECK(!size.IsEmpty()); DCHECK(!size.IsEmpty());
DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, DCHECK(VerifySizeInBytesInternal<T>(size, format, false));
false)); return SizeInBytesInternal<T>(size, format, false);
return BytesInternal<T>(size.width(), size.height(), format, true, false);
} }
template <typename T> template <typename T>
...@@ -195,8 +185,8 @@ T ResourceSizes::UncheckedWidthInBytesAligned(int width, ...@@ -195,8 +185,8 @@ T ResourceSizes::UncheckedWidthInBytesAligned(int width,
ResourceFormat format) { ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
DCHECK_GT(width, 0); DCHECK_GT(width, 0);
DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, true)); DCHECK(VerifyWidthInBytesInternal<T>(width, format, true));
return BytesInternal<T>(width, 0, format, false, true); return WidthInBytesInternal<T>(width, format, true);
} }
template <typename T> template <typename T>
...@@ -204,9 +194,8 @@ T ResourceSizes::UncheckedSizeInBytesAligned(const gfx::Size& size, ...@@ -204,9 +194,8 @@ T ResourceSizes::UncheckedSizeInBytesAligned(const gfx::Size& size,
ResourceFormat format) { ResourceFormat format) {
VerifyType<T>(); VerifyType<T>();
CHECK(!size.IsEmpty()); CHECK(!size.IsEmpty());
DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, DCHECK(VerifySizeInBytesInternal<T>(size, format, true));
true)); return SizeInBytesInternal<T>(size, format, true);
return BytesInternal<T>(size.width(), size.height(), format, true, true);
} }
template <typename T> template <typename T>
...@@ -217,61 +206,107 @@ void ResourceSizes::VerifyType() { ...@@ -217,61 +206,107 @@ void ResourceSizes::VerifyType() {
} }
template <typename T> template <typename T>
bool ResourceSizes::VerifyFitsInBytesInternal(int width, bool ResourceSizes::VerifyWidthInBytesInternal(int width,
int height, ResourceFormat format,
bool aligned) {
T ignored;
return MaybeWidthInBytesInternal(width, format, aligned, &ignored);
}
template <typename T>
bool ResourceSizes::VerifySizeInBytesInternal(const gfx::Size& size,
ResourceFormat format, ResourceFormat format,
bool verify_size,
bool aligned) { bool aligned) {
base::CheckedNumeric<T> checked_value = BitsPerPixel(format); T ignored;
checked_value *= width; return MaybeSizeInBytesInternal(size, format, aligned, &ignored);
if (!checked_value.IsValid()) }
template <typename T>
bool ResourceSizes::MaybeWidthInBytesInternal(int width,
ResourceFormat format,
bool aligned,
T* bytes) {
base::CheckedNumeric<T> bits_per_row = BitsPerPixel(format);
bits_per_row *= width;
if (!bits_per_row.IsValid())
return false; return false;
// Roundup bits to byte (8 bits) boundary. If width is 3 and BitsPerPixel is // Roundup bits to byte (8 bits) boundary. If width is 3 and BitsPerPixel is
// 4, then it should return 16, so that row pixels do not get truncated. // 4, then it should return 16, so that row pixels do not get truncated.
checked_value = if (!MaybeRound<T>(&bits_per_row, 8))
cc::MathUtil::UncheckedRoundUp<T>(checked_value.ValueOrDie(), 8); return false;
// Convert to bytes by dividing by 8. This can't fail as we've rounded to a
// multiple of 8 above.
base::CheckedNumeric<T> bytes_per_row = bits_per_row / 8;
DCHECK(bytes_per_row.IsValid());
// If aligned is true, bytes are aligned on 4-bytes boundaries for upload // If aligned is true, bytes are aligned on 4-bytes boundaries for upload
// performance, assuming that GL_PACK_ALIGNMENT or GL_UNPACK_ALIGNMENT have // performance, assuming that GL_PACK_ALIGNMENT or GL_UNPACK_ALIGNMENT have
// not changed from default. // not changed from default.
if (aligned) { if (aligned) {
checked_value /= 8; // This can't fail as we've just divided by 8, so we can always round up to
if (!checked_value.IsValid()) // the nearest multiple of 4.
return false; bool succeeded = MaybeRound<T>(&bytes_per_row, 4);
checked_value = DCHECK(succeeded);
cc::MathUtil::UncheckedRoundUp<T>(checked_value.ValueOrDie(), 4);
checked_value *= 8;
} }
if (verify_size) *bytes = bytes_per_row.ValueOrDie();
checked_value *= height; return true;
if (!checked_value.IsValid()) }
template <typename T>
bool ResourceSizes::MaybeSizeInBytesInternal(const gfx::Size& size,
ResourceFormat format,
bool aligned,
T* bytes) {
T width_in_bytes;
if (!MaybeWidthInBytesInternal<T>(size.width(), format, aligned,
&width_in_bytes)) {
return false; return false;
T value = checked_value.ValueOrDie(); }
if ((value % 8) != 0)
base::CheckedNumeric<T> total_bytes = width_in_bytes;
total_bytes *= size.height();
if (!total_bytes.IsValid())
return false; return false;
*bytes = total_bytes.ValueOrDie();
return true; return true;
} }
template <typename T> template <typename T>
T ResourceSizes::BytesInternal(int width, T ResourceSizes::WidthInBytesInternal(int width,
int height, ResourceFormat format,
ResourceFormat format, bool aligned) {
bool verify_size,
bool aligned) {
T bytes = BitsPerPixel(format); T bytes = BitsPerPixel(format);
bytes *= width; bytes *= width;
bytes = cc::MathUtil::UncheckedRoundUp<T>(bytes, 8); bytes = cc::MathUtil::UncheckedRoundUp<T>(bytes, 8);
bytes /= 8; bytes /= 8;
if (aligned) if (aligned)
bytes = cc::MathUtil::UncheckedRoundUp<T>(bytes, 4); bytes = cc::MathUtil::UncheckedRoundUp<T>(bytes, 4);
if (verify_size) return bytes;
bytes *= height; }
template <typename T>
T ResourceSizes::SizeInBytesInternal(const gfx::Size& size,
ResourceFormat format,
bool aligned) {
T bytes = WidthInBytesInternal<T>(size.width(), format, aligned);
bytes *= size.height();
return bytes; return bytes;
} }
template <typename T>
bool ResourceSizes::MaybeRound(base::CheckedNumeric<T>* value, T mul) {
DCHECK(value->IsValid());
T to_round = value->ValueOrDie();
if (!cc::MathUtil::VerifyRoundup<T>(to_round, mul))
return false;
*value = cc::MathUtil::UncheckedRoundUp<T>(to_round, mul);
return true;
}
} // namespace viz } // namespace viz
#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SIZES_H_ #endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_SIZES_H_
...@@ -166,5 +166,31 @@ TEST_F(ResourceUtilTest, SizeInBytesOverflow) { ...@@ -166,5 +166,31 @@ TEST_F(ResourceUtilTest, SizeInBytesOverflow) {
EXPECT_TRUE(ResourceSizes::VerifySizeInBytes<int>(size, RGBA_4444)); EXPECT_TRUE(ResourceSizes::VerifySizeInBytes<int>(size, RGBA_4444));
} }
TEST_F(ResourceUtilTest, WidthOverflowDoesNotCrash) {
gfx::Size size(0x20000000, 1);
// 0x20000000 * 4 = 0x80000000 which overflows int. Should return false, not
// crash.
int bytes;
EXPECT_FALSE(
ResourceSizes::MaybeWidthInBytes<int>(size.width(), BGRA_8888, &bytes));
EXPECT_FALSE(ResourceSizes::MaybeSizeInBytes<int>(size, BGRA_8888, &bytes));
}
// Checks that we do not incorrectly indicate that a size has overflowed when
// only the size in bits overflows, but not the size in bytes.
TEST_F(ResourceUtilTest, SizeInBitsOverflowBytesOk) {
gfx::Size size(10000, 10000);
// 8192 * 8192 * 32 = 0x80000000, overflows int.
// Bytes are /8 and do not overflow.
EXPECT_TRUE(ResourceSizes::VerifySizeInBytes<int>(size, BGRA_8888));
}
// Checks that we correctly identify overflow in cases caused by rounding.
TEST_F(ResourceUtilTest, RoundingOverflows) {
gfx::Size size(0x1FFFFFFF, 1);
// 0x1FFFFFFF * 4 = 0x7FFFFFFC. Will overflow when rounded up.
EXPECT_FALSE(ResourceSizes::VerifySizeInBytes<int>(size, ETC1));
}
} // namespace } // namespace
} // namespace viz } // namespace viz
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment