Fix MulDivSaturate to avoid overflow
Thanks to Caolan https://gerrit.libreoffice.org/c/core/+/110839/comments/5c47cab2_b0523546 > oss-fuzz has come up with a case of n 9223372036854775807 m 72 d 127 > > include/o3tl/unit_conversion.hxx:105:28: runtime error: signed integer > overflow: 9223372036854775807 + 63 cannot be represented in type 'long' Change-Id: I0fe26f6c854a90cf577a60528d15f3da5471b914 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141711 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
parent
87aef8ff9f
commit
635a0b426e
3 changed files with 17 additions and 6 deletions
|
@ -27,7 +27,7 @@
|
|||
namespace o3tl
|
||||
{
|
||||
|
||||
template <typename T> inline T saturating_add(T a, T b)
|
||||
template <typename T> inline constexpr T saturating_add(T a, T b)
|
||||
{
|
||||
if (b >= 0) {
|
||||
if (a <= std::numeric_limits<T>::max() - b) {
|
||||
|
@ -44,7 +44,7 @@ template <typename T> inline T saturating_add(T a, T b)
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T> inline T saturating_sub(T a, T b)
|
||||
template <typename T> inline constexpr T saturating_sub(T a, T b)
|
||||
{
|
||||
if (b >= 0) {
|
||||
if (a >= std::numeric_limits<T>::min() + b) {
|
||||
|
|
|
@ -98,11 +98,20 @@ constexpr sal_Int64 MulDiv(I n, sal_Int64 m, sal_Int64 d, bool& bOverflow, sal_I
|
|||
template <typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
|
||||
constexpr sal_Int64 MulDivSaturate(I n, sal_Int64 m, sal_Int64 d)
|
||||
{
|
||||
if (!isBetween(n, (SAL_MIN_INT64 + d / 2) / m, (SAL_MAX_INT64 - d / 2) / m))
|
||||
if (sal_Int64 d_2 = d / 2; !isBetween(n, (SAL_MIN_INT64 + d_2) / m, (SAL_MAX_INT64 - d_2) / m))
|
||||
{
|
||||
if (m > d && !isBetween(n, SAL_MIN_INT64 / m * d + d / 2, SAL_MAX_INT64 / m * d - d / 2))
|
||||
return n > 0 ? SAL_MAX_INT64 : SAL_MIN_INT64; // saturate
|
||||
return (n >= 0 ? n + d / 2 : n - d / 2) / d * m; // divide before multiplication
|
||||
if (n >= 0)
|
||||
{
|
||||
if (m > d && std::make_unsigned_t<I>(n) > sal_uInt64(SAL_MAX_INT64 / m * d - d_2))
|
||||
return SAL_MAX_INT64; // saturate
|
||||
return saturating_add<sal_uInt64>(n, d_2) / d * m; // divide before multiplication
|
||||
}
|
||||
else if constexpr (std::is_signed_v<I>) // n < 0; don't compile for unsigned n
|
||||
{
|
||||
if (m > d && n < SAL_MIN_INT64 / m * d + d_2)
|
||||
return SAL_MIN_INT64; // saturate
|
||||
return saturating_sub<sal_Int64>(n, d_2) / d * m; // divide before multiplication
|
||||
}
|
||||
}
|
||||
return MulDiv(n, m, d);
|
||||
}
|
||||
|
|
|
@ -878,4 +878,6 @@ static_assert(o3tl::toTwips(20, o3tl::Length::mm) == 1134);
|
|||
// 847 100thmm used to represent 24pt
|
||||
static_assert(o3tl::convert(24, o3tl::Length::pt, o3tl::Length::mm100) == 847);
|
||||
|
||||
static_assert(o3tl::convertSaturate(SAL_MAX_INT64, 72, 127) == 5228998320106644552); // no overflow
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
|
|
Loading…
Reference in a new issue