diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /security/sandbox/chromium/base/numerics | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
11 files changed, 3356 insertions, 0 deletions
diff --git a/security/sandbox/chromium/base/numerics/checked_math.h b/security/sandbox/chromium/base/numerics/checked_math.h new file mode 100644 index 0000000000..ede3344f82 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/checked_math.h @@ -0,0 +1,393 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CHECKED_MATH_H_ +#define BASE_NUMERICS_CHECKED_MATH_H_ + +#include <stddef.h> + +#include <limits> +#include <type_traits> + +#include "base/numerics/checked_math_impl.h" + +namespace base { +namespace internal { + +template <typename T> +class CheckedNumeric { + static_assert(std::is_arithmetic<T>::value, + "CheckedNumeric<T>: T must be a numeric type."); + + public: + using type = T; + + constexpr CheckedNumeric() = default; + + // Copy constructor. + template <typename Src> + constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} + + template <typename Src> + friend class CheckedNumeric; + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template <typename Src> + constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) + : state_(value) { + static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template <typename Src> + constexpr CheckedNumeric( + StrictNumeric<Src> value) // NOLINT(runtime/explicit) + : state_(static_cast<Src>(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template <typename Dst = T> + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType<Dst>(state_.value()); + } + + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template <typename Dst> +#if defined(__clang__) || defined(__GNUC__) + __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + _Check_return_ +#endif + constexpr bool + AssignIfValid(Dst* result) const { + return BASE_NUMERICS_LIKELY(IsValid<Dst>()) + ? ((*result = static_cast<Dst>(state_.value())), true) + : false; + } + + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template <typename Dst = T, class CheckHandler = CheckOnFailure> + constexpr StrictNumeric<Dst> ValueOrDie() const { + return BASE_NUMERICS_LIKELY(IsValid<Dst>()) + ? static_cast<Dst>(state_.value()) + : CheckHandler::template HandleFailure<Dst>(); + } + + // ValueOrDefault(T default_value) - A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template <typename Dst = T, typename Src> + constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const { + return BASE_NUMERICS_LIKELY(IsValid<Dst>()) + ? static_cast<Dst>(state_.value()) + : checked_cast<Dst>(default_value); + } + + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template <typename Dst> + constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const { + return *this; + } + + // This friend method is available solely for providing more detailed logging + // in the the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template <typename U> + friend U GetNumericValueForTest(const CheckedNumeric<U>& src); + + // Prototypes for the supported arithmetic operator overloads. + template <typename Src> + constexpr CheckedNumeric& operator+=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator-=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator*=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator/=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator%=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator<<=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator>>=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator&=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator|=(const Src rhs); + template <typename Src> + constexpr CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + // The negation of two's complement int min is int min, so we simply + // check for that in the constexpr case. + // We use an optimized code path for a known run-time variable. + return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value || + std::is_floating_point<T>::value + ? CheckedNumeric<T>( + NegateWrapper(state_.value()), + IsValid() && (!std::is_signed<T>::value || + std::is_floating_point<T>::value || + NegateWrapper(state_.value()) != + std::numeric_limits<T>::lowest())) + : FastRuntimeNegate(); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric<decltype(InvertWrapper(T()))>( + InvertWrapper(state_.value()), IsValid()); + } + + constexpr CheckedNumeric Abs() const { + return !IsValueNegative(state_.value()) ? *this : -*this; + } + + template <typename U> + constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max( + const U rhs) const { + using R = typename UnderlyingType<U>::type; + using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric<result_type>( + static_cast<result_type>( + IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs)) + ? state_.value() + : Wrapper<U>::value(rhs)), + state_.is_valid() && Wrapper<U>::is_valid(rhs)); + } + + template <typename U> + constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min( + const U rhs) const { + using R = typename UnderlyingType<U>::type; + using result_type = typename MathWrapper<CheckedMinOp, T, U>::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric<result_type>( + static_cast<result_type>( + IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs)) + ? state_.value() + : Wrapper<U>::value(rhs)), + state_.is_valid() && Wrapper<U>::is_valid(rhs)); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> + UnsignedAbs() const { + return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); + } + + constexpr CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + constexpr CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template <template <typename, typename, typename> class M, + typename L, + typename R> + static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) { + using Math = typename MathWrapper<M, L, R>::math; + T result = 0; + bool is_valid = + Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && + Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); + return CheckedNumeric<T>(result, is_valid); + } + + // Assignment arithmetic operations. + template <template <typename, typename, typename> class M, typename R> + constexpr CheckedNumeric& MathOp(const R rhs) { + using Math = typename MathWrapper<M, T, R>::math; + T result = 0; // Using T as the destination saves a range check. + bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && + Math::Do(state_.value(), Wrapper<R>::value(rhs), &result); + *this = CheckedNumeric<T>(result, is_valid); + return *this; + } + + private: + CheckedNumericState<T> state_; + + CheckedNumeric FastRuntimeNegate() const { + T result; + bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result); + return CheckedNumeric<T>(result, IsValid() && success); + } + + template <typename Src> + constexpr CheckedNumeric(Src value, bool is_valid) + : state_(value, is_valid) {} + + // These wrappers allow us to handle state the same way for both + // CheckedNumeric and POD arithmetic types. + template <typename Src> + struct Wrapper { + static constexpr bool is_valid(Src) { return true; } + static constexpr Src value(Src value) { return value; } + }; + + template <typename Src> + struct Wrapper<CheckedNumeric<Src>> { + static constexpr bool is_valid(const CheckedNumeric<Src> v) { + return v.IsValid(); + } + static constexpr Src value(const CheckedNumeric<Src> v) { + return v.state_.value(); + } + }; + + template <typename Src> + struct Wrapper<StrictNumeric<Src>> { + static constexpr bool is_valid(const StrictNumeric<Src>) { return true; } + static constexpr Src value(const StrictNumeric<Src> v) { + return static_cast<Src>(v); + } + }; +}; + +// Convenience functions to avoid the ugly template disambiguator syntax. +template <typename Dst, typename Src> +constexpr bool IsValidForType(const CheckedNumeric<Src> value) { + return value.template IsValid<Dst>(); +} + +template <typename Dst, typename Src> +constexpr StrictNumeric<Dst> ValueOrDieForType( + const CheckedNumeric<Src> value) { + return value.template ValueOrDie<Dst>(); +} + +template <typename Dst, typename Src, typename Default> +constexpr StrictNumeric<Dst> ValueOrDefaultForType( + const CheckedNumeric<Src> value, + const Default default_value) { + return value.template ValueOrDefault<Dst>(default_value); +} + +// Convience wrapper to return a new CheckedNumeric from the provided arithmetic +// or CheckedNumericType. +template <typename T> +constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum( + const T value) { + return value; +} + +// These implement the variadic wrapper for the math operations. +template <template <typename, typename, typename> class M, + typename L, + typename R> +constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp( + const L lhs, + const R rhs) { + using Math = typename MathWrapper<M, L, R>::math; + return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs, + rhs); +} + +// General purpose wrapper template for arithmetic operations. +template <template <typename, typename, typename> class M, + typename L, + typename R, + typename... Args> +constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type> +CheckMathOp(const L lhs, const R rhs, const Args... args) { + return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...); +} + +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=) +BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max) +BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min) + +// These are some extra StrictNumeric operators to support simple pointer +// arithmetic with our result types. Since wrapping on a pointer is always +// bad, we trigger the CHECK condition here. +template <typename L, typename R> +L* operator+(L* lhs, const StrictNumeric<R> rhs) { + uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs), + CheckMul(sizeof(L), static_cast<R>(rhs))) + .template ValueOrDie<uintptr_t>(); + return reinterpret_cast<L*>(result); +} + +template <typename L, typename R> +L* operator-(L* lhs, const StrictNumeric<R> rhs) { + uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs), + CheckMul(sizeof(L), static_cast<R>(rhs))) + .template ValueOrDie<uintptr_t>(); + return reinterpret_cast<L*>(result); +} + +} // namespace internal + +using internal::CheckedNumeric; +using internal::IsValidForType; +using internal::ValueOrDieForType; +using internal::ValueOrDefaultForType; +using internal::MakeCheckedNum; +using internal::CheckMax; +using internal::CheckMin; +using internal::CheckAdd; +using internal::CheckSub; +using internal::CheckMul; +using internal::CheckDiv; +using internal::CheckMod; +using internal::CheckLsh; +using internal::CheckRsh; +using internal::CheckAnd; +using internal::CheckOr; +using internal::CheckXor; + +} // namespace base + +#endif // BASE_NUMERICS_CHECKED_MATH_H_ diff --git a/security/sandbox/chromium/base/numerics/checked_math_impl.h b/security/sandbox/chromium/base/numerics/checked_math_impl.h new file mode 100644 index 0000000000..e083389ebf --- /dev/null +++ b/security/sandbox/chromium/base/numerics/checked_math_impl.h @@ -0,0 +1,567 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_ +#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <climits> +#include <cmath> +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math_shared_impl.h" + +namespace base { +namespace internal { + +template <typename T> +constexpr bool CheckedAddImpl(T x, T y, T* result) { + static_assert(std::is_integral<T>::value, "Type must be integral"); + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + using UnsignedDst = typename std::make_unsigned<T>::type; + using SignedDst = typename std::make_signed<T>::type; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); + *result = static_cast<T>(uresult); + // Addition is valid if the sign of (x + y) is equal to either that of x or + // that of y. + return (std::is_signed<T>::value) + ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0 + : uresult >= uy; // Unsigned is either valid or underflow. +} + +template <typename T, typename U, class Enable = void> +struct CheckedAddOp {}; + +template <typename T, typename U> +struct CheckedAddOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + // TODO(jschuh) Make this "constexpr if" once we're C++17. + if (CheckedAddFastOp<T, U>::is_supported) + return CheckedAddFastOp<T, U>::Do(x, y, result); + + // Double the underlying type up to a full machine word. + using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type; + using Promotion = + typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value > + IntegerBitsPlusSign<intptr_t>::value), + typename BigEnoughPromotion<T, U>::type, + FastPromotion>::type; + // Fail if either operand is out of range for the promoted type. + // TODO(jschuh): This could be made to work for a broader range of values. + if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) || + !IsValueInRangeForNumericType<Promotion>(y))) { + return false; + } + + Promotion presult = {}; + bool is_valid = true; + if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { + presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); + } else { + is_valid = CheckedAddImpl(static_cast<Promotion>(x), + static_cast<Promotion>(y), &presult); + } + *result = static_cast<V>(presult); + return is_valid && IsValueInRangeForNumericType<V>(presult); + } +}; + +template <typename T> +constexpr bool CheckedSubImpl(T x, T y, T* result) { + static_assert(std::is_integral<T>::value, "Type must be integral"); + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + using UnsignedDst = typename std::make_unsigned<T>::type; + using SignedDst = typename std::make_signed<T>::type; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); + *result = static_cast<T>(uresult); + // Subtraction is valid if either x and y have same sign, or (x-y) and x have + // the same sign. + return (std::is_signed<T>::value) + ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0 + : x >= y; +} + +template <typename T, typename U, class Enable = void> +struct CheckedSubOp {}; + +template <typename T, typename U> +struct CheckedSubOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + // TODO(jschuh) Make this "constexpr if" once we're C++17. + if (CheckedSubFastOp<T, U>::is_supported) + return CheckedSubFastOp<T, U>::Do(x, y, result); + + // Double the underlying type up to a full machine word. + using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type; + using Promotion = + typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value > + IntegerBitsPlusSign<intptr_t>::value), + typename BigEnoughPromotion<T, U>::type, + FastPromotion>::type; + // Fail if either operand is out of range for the promoted type. + // TODO(jschuh): This could be made to work for a broader range of values. + if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) || + !IsValueInRangeForNumericType<Promotion>(y))) { + return false; + } + + Promotion presult = {}; + bool is_valid = true; + if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { + presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); + } else { + is_valid = CheckedSubImpl(static_cast<Promotion>(x), + static_cast<Promotion>(y), &presult); + } + *result = static_cast<V>(presult); + return is_valid && IsValueInRangeForNumericType<V>(presult); + } +}; + +template <typename T> +constexpr bool CheckedMulImpl(T x, T y, T* result) { + static_assert(std::is_integral<T>::value, "Type must be integral"); + // Since the value of x*y is potentially undefined if we have a signed type, + // we compute it using the unsigned type of the same size. + using UnsignedDst = typename std::make_unsigned<T>::type; + using SignedDst = typename std::make_signed<T>::type; + const UnsignedDst ux = SafeUnsignedAbs(x); + const UnsignedDst uy = SafeUnsignedAbs(y); + UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy); + const bool is_negative = + std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0; + *result = is_negative ? 0 - uresult : uresult; + // We have a fast out for unsigned identity or zero on the second operand. + // After that it's an unsigned overflow check on the absolute value, with + // a +1 bound for a negative result. + return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) || + ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy; +} + +template <typename T, typename U, class Enable = void> +struct CheckedMulOp {}; + +template <typename T, typename U> +struct CheckedMulOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + // TODO(jschuh) Make this "constexpr if" once we're C++17. + if (CheckedMulFastOp<T, U>::is_supported) + return CheckedMulFastOp<T, U>::Do(x, y, result); + + using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; + // Verify the destination type can hold the result (always true for 0). + if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) || + !IsValueInRangeForNumericType<Promotion>(y)) && + x && y)) { + return false; + } + + Promotion presult = {}; + bool is_valid = true; + if (CheckedMulFastOp<Promotion, Promotion>::is_supported) { + // The fast op may be available with the promoted type. + is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult); + } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { + presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); + } else { + is_valid = CheckedMulImpl(static_cast<Promotion>(x), + static_cast<Promotion>(y), &presult); + } + *result = static_cast<V>(presult); + return is_valid && IsValueInRangeForNumericType<V>(presult); + } +}; + +// Division just requires a check for a zero denominator or an invalid negation +// on signed min/-1. +template <typename T, typename U, class Enable = void> +struct CheckedDivOp {}; + +template <typename T, typename U> +struct CheckedDivOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + if (BASE_NUMERICS_UNLIKELY(!y)) + return false; + + // The overflow check can be compiled away if we don't have the exact + // combination of types needed to trigger this case. + using Promotion = typename BigEnoughPromotion<T, U>::type; + if (BASE_NUMERICS_UNLIKELY( + (std::is_signed<T>::value && std::is_signed<U>::value && + IsTypeInRangeForNumericType<T, Promotion>::value && + static_cast<Promotion>(x) == + std::numeric_limits<Promotion>::lowest() && + y == static_cast<U>(-1)))) { + return false; + } + + // This branch always compiles away if the above branch wasn't removed. + if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) || + !IsValueInRangeForNumericType<Promotion>(y)) && + x)) { + return false; + } + + Promotion presult = Promotion(x) / Promotion(y); + *result = static_cast<V>(presult); + return IsValueInRangeForNumericType<V>(presult); + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedModOp {}; + +template <typename T, typename U> +struct CheckedModOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + using Promotion = typename BigEnoughPromotion<T, U>::type; + if (BASE_NUMERICS_LIKELY(y)) { + Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y); + *result = static_cast<Promotion>(presult); + return IsValueInRangeForNumericType<V>(presult); + } + return false; + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedLshOp {}; + +// Left shift. Shifts less than 0 or greater than or equal to the number +// of bits in the promoted type are undefined. Shifts of negative values +// are undefined. Otherwise it is defined when the result fits. +template <typename T, typename U> +struct CheckedLshOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = T; + template <typename V> + static constexpr bool Do(T x, U shift, V* result) { + // Disallow negative numbers and verify the shift is in bounds. + if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) && + as_unsigned(shift) < + as_unsigned(std::numeric_limits<T>::digits))) { + // Shift as unsigned to avoid undefined behavior. + *result = static_cast<V>(as_unsigned(x) << shift); + // If the shift can be reversed, we know it was valid. + return *result >> shift == x; + } + + // Handle the legal corner-case of a full-width signed shift of zero. + return std::is_signed<T>::value && !x && + as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits); + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedRshOp {}; + +// Right shift. Shifts less than 0 or greater than or equal to the number +// of bits in the promoted type are undefined. Otherwise, it is always defined, +// but a right shift of a negative value is implementation-dependent. +template <typename T, typename U> +struct CheckedRshOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = T; + template <typename V> + static bool Do(T x, U shift, V* result) { + // Use the type conversion push negative values out of range. + if (BASE_NUMERICS_LIKELY(as_unsigned(shift) < + IntegerBitsPlusSign<T>::value)) { + T tmp = x >> shift; + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } + return false; + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedAndOp {}; + +// For simplicity we support only unsigned integer results. +template <typename T, typename U> +struct CheckedAndOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y); + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedOrOp {}; + +// For simplicity we support only unsigned integers. +template <typename T, typename U> +struct CheckedOrOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y); + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } +}; + +template <typename T, typename U, class Enable = void> +struct CheckedXorOp {}; + +// For simplicity we support only unsigned integers. +template <typename T, typename U> +struct CheckedXorOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y); + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } +}; + +// Max doesn't really need to be implemented this way because it can't fail, +// but it makes the code much cleaner to use the MathOp wrappers. +template <typename T, typename U, class Enable = void> +struct CheckedMaxOp {}; + +template <typename T, typename U> +struct CheckedMaxOp< + T, + U, + typename std::enable_if<std::is_arithmetic<T>::value && + std::is_arithmetic<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x) + : static_cast<result_type>(y); + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } +}; + +// Min doesn't really need to be implemented this way because it can't fail, +// but it makes the code much cleaner to use the MathOp wrappers. +template <typename T, typename U, class Enable = void> +struct CheckedMinOp {}; + +template <typename T, typename U> +struct CheckedMinOp< + T, + U, + typename std::enable_if<std::is_arithmetic<T>::value && + std::is_arithmetic<U>::value>::type> { + using result_type = typename LowestValuePromotion<T, U>::type; + template <typename V> + static constexpr bool Do(T x, U y, V* result) { + result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x) + : static_cast<result_type>(y); + *result = static_cast<V>(tmp); + return IsValueInRangeForNumericType<V>(tmp); + } +}; + +// This is just boilerplate that wraps the standard floating point arithmetic. +// A macro isn't the nicest solution, but it beats rewriting these repeatedly. +#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ + template <typename T, typename U> \ + struct Checked##NAME##Op< \ + T, U, \ + typename std::enable_if<std::is_floating_point<T>::value || \ + std::is_floating_point<U>::value>::type> { \ + using result_type = typename MaxExponentPromotion<T, U>::type; \ + template <typename V> \ + static constexpr bool Do(T x, U y, V* result) { \ + using Promotion = typename MaxExponentPromotion<T, U>::type; \ + Promotion presult = x OP y; \ + *result = static_cast<V>(presult); \ + return IsValueInRangeForNumericType<V>(presult); \ + } \ + }; + +BASE_FLOAT_ARITHMETIC_OPS(Add, +) +BASE_FLOAT_ARITHMETIC_OPS(Sub, -) +BASE_FLOAT_ARITHMETIC_OPS(Mul, *) +BASE_FLOAT_ARITHMETIC_OPS(Div, /) + +#undef BASE_FLOAT_ARITHMETIC_OPS + +// Floats carry around their validity state with them, but integers do not. So, +// we wrap the underlying value in a specialization in order to hide that detail +// and expose an interface via accessors. +enum NumericRepresentation { + NUMERIC_INTEGER, + NUMERIC_FLOATING, + NUMERIC_UNKNOWN +}; + +template <typename NumericType> +struct GetNumericRepresentation { + static const NumericRepresentation value = + std::is_integral<NumericType>::value + ? NUMERIC_INTEGER + : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING + : NUMERIC_UNKNOWN); +}; + +template <typename T, + NumericRepresentation type = GetNumericRepresentation<T>::value> +class CheckedNumericState {}; + +// Integrals require quite a bit of additional housekeeping to manage state. +template <typename T> +class CheckedNumericState<T, NUMERIC_INTEGER> { + private: + // is_valid_ precedes value_ because member intializers in the constructors + // are evaluated in field order, and is_valid_ must be read when initializing + // value_. + bool is_valid_; + T value_; + + // Ensures that a type conversion does not trigger undefined behavior. + template <typename Src> + static constexpr T WellDefinedConversionOrZero(const Src value, + const bool is_valid) { + using SrcType = typename internal::UnderlyingType<Src>::type; + return (std::is_integral<SrcType>::value || is_valid) + ? static_cast<T>(value) + : static_cast<T>(0); + } + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + constexpr CheckedNumericState() : is_valid_(true), value_(0) {} + + template <typename Src> + constexpr CheckedNumericState(Src value, bool is_valid) + : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)), + value_(WellDefinedConversionOrZero(value, is_valid_)) { + static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); + } + + // Copy constructor. + template <typename Src> + constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) + : is_valid_(rhs.IsValid()), + value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {} + + template <typename Src> + constexpr explicit CheckedNumericState(Src value) + : is_valid_(IsValueInRangeForNumericType<T>(value)), + value_(WellDefinedConversionOrZero(value, is_valid_)) {} + + constexpr bool is_valid() const { return is_valid_; } + constexpr T value() const { return value_; } +}; + +// Floating points maintain their own validity, but need translation wrappers. +template <typename T> +class CheckedNumericState<T, NUMERIC_FLOATING> { + private: + T value_; + + // Ensures that a type conversion does not trigger undefined behavior. + template <typename Src> + static constexpr T WellDefinedConversionOrNaN(const Src value, + const bool is_valid) { + using SrcType = typename internal::UnderlyingType<Src>::type; + return (StaticDstRangeRelationToSrcRange<T, SrcType>::value == + NUMERIC_RANGE_CONTAINED || + is_valid) + ? static_cast<T>(value) + : std::numeric_limits<T>::quiet_NaN(); + } + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + constexpr CheckedNumericState() : value_(0.0) {} + + template <typename Src> + constexpr CheckedNumericState(Src value, bool is_valid) + : value_(WellDefinedConversionOrNaN(value, is_valid)) {} + + template <typename Src> + constexpr explicit CheckedNumericState(Src value) + : value_(WellDefinedConversionOrNaN( + value, + IsValueInRangeForNumericType<T>(value))) {} + + // Copy constructor. + template <typename Src> + constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) + : value_(WellDefinedConversionOrNaN( + rhs.value(), + rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {} + + constexpr bool is_valid() const { + // Written this way because std::isfinite is not reliably constexpr. + return MustTreatAsConstexpr(value_) + ? value_ <= std::numeric_limits<T>::max() && + value_ >= std::numeric_limits<T>::lowest() + : std::isfinite(value_); + } + constexpr T value() const { return value_; } +}; + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/clamped_math.h b/security/sandbox/chromium/base/numerics/clamped_math.h new file mode 100644 index 0000000000..37a4cfd22a --- /dev/null +++ b/security/sandbox/chromium/base/numerics/clamped_math.h @@ -0,0 +1,264 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CLAMPED_MATH_H_ +#define BASE_NUMERICS_CLAMPED_MATH_H_ + +#include <stddef.h> + +#include <limits> +#include <type_traits> + +#include "base/numerics/clamped_math_impl.h" + +namespace base { +namespace internal { + +template <typename T> +class ClampedNumeric { + static_assert(std::is_arithmetic<T>::value, + "ClampedNumeric<T>: T must be a numeric type."); + + public: + using type = T; + + constexpr ClampedNumeric() : value_(0) {} + + // Copy constructor. + template <typename Src> + constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs) + : value_(saturated_cast<T>(rhs.value_)) {} + + template <typename Src> + friend class ClampedNumeric; + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to ClampedNumerics to make them easier to use. + template <typename Src> + constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit) + : value_(saturated_cast<T>(value)) { + static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template <typename Src> + constexpr ClampedNumeric( + StrictNumeric<Src> value) // NOLINT(runtime/explicit) + : value_(saturated_cast<T>(static_cast<Src>(value))) {} + + // Returns a ClampedNumeric of the specified type, cast from the current + // ClampedNumeric, and saturated to the destination type. + template <typename Dst> + constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const { + return *this; + } + + // Prototypes for the supported arithmetic operator overloads. + template <typename Src> + constexpr ClampedNumeric& operator+=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator-=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator*=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator/=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator%=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator<<=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator>>=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator&=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator|=(const Src rhs); + template <typename Src> + constexpr ClampedNumeric& operator^=(const Src rhs); + + constexpr ClampedNumeric operator-() const { + // The negation of two's complement int min is int min, so that's the + // only overflow case where we will saturate. + return ClampedNumeric<T>(SaturatedNegWrapper(value_)); + } + + constexpr ClampedNumeric operator~() const { + return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_)); + } + + constexpr ClampedNumeric Abs() const { + // The negation of two's complement int min is int min, so that's the + // only overflow case where we will saturate. + return ClampedNumeric<T>(SaturatedAbsWrapper(value_)); + } + + template <typename U> + constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max( + const U rhs) const { + using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type; + return ClampedNumeric<result_type>( + ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs))); + } + + template <typename U> + constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min( + const U rhs) const { + using result_type = typename MathWrapper<ClampedMinOp, T, U>::type; + return ClampedNumeric<result_type>( + ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs))); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type> + UnsignedAbs() const { + return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>( + SafeUnsignedAbs(value_)); + } + + constexpr ClampedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr ClampedNumeric operator++(int) { + ClampedNumeric value = *this; + *this += 1; + return value; + } + + constexpr ClampedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr ClampedNumeric operator--(int) { + ClampedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the ClampedNumerics. + // Binary arithmetic operations. + template <template <typename, typename, typename> class M, + typename L, + typename R> + static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) { + using Math = typename MathWrapper<M, L, R>::math; + return ClampedNumeric<T>( + Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs))); + } + + // Assignment arithmetic operations. + template <template <typename, typename, typename> class M, typename R> + constexpr ClampedNumeric& MathOp(const R rhs) { + using Math = typename MathWrapper<M, T, R>::math; + *this = + ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs))); + return *this; + } + + template <typename Dst> + constexpr operator Dst() const { + return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>( + value_); + } + + // This method extracts the raw integer value without saturating it to the + // destination type as the conversion operator does. This is useful when + // e.g. assigning to an auto type or passing as a deduced template parameter. + constexpr T RawValue() const { return value_; } + + private: + T value_; + + // These wrappers allow us to handle state the same way for both + // ClampedNumeric and POD arithmetic types. + template <typename Src> + struct Wrapper { + static constexpr Src value(Src value) { + return static_cast<typename UnderlyingType<Src>::type>(value); + } + }; +}; + +// Convience wrapper to return a new ClampedNumeric from the provided arithmetic +// or ClampedNumericType. +template <typename T> +constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum( + const T value) { + return value; +} + +#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS +// Overload the ostream output operator to make logging work nicely. +template <typename T> +std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) { + os << static_cast<T>(value); + return os; +} +#endif + +// These implement the variadic wrapper for the math operations. +template <template <typename, typename, typename> class M, + typename L, + typename R> +constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp( + const L lhs, + const R rhs) { + using Math = typename MathWrapper<M, L, R>::math; + return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs, + rhs); +} + +// General purpose wrapper template for arithmetic operations. +template <template <typename, typename, typename> class M, + typename L, + typename R, + typename... Args> +constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type> +ClampMathOp(const L lhs, const R rhs, const Args... args) { + return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...); +} + +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=) +BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max) +BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==) +BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=) + +} // namespace internal + +using internal::ClampedNumeric; +using internal::MakeClampedNum; +using internal::ClampMax; +using internal::ClampMin; +using internal::ClampAdd; +using internal::ClampSub; +using internal::ClampMul; +using internal::ClampDiv; +using internal::ClampMod; +using internal::ClampLsh; +using internal::ClampRsh; +using internal::ClampAnd; +using internal::ClampOr; +using internal::ClampXor; + +} // namespace base + +#endif // BASE_NUMERICS_CLAMPED_MATH_H_ diff --git a/security/sandbox/chromium/base/numerics/clamped_math_impl.h b/security/sandbox/chromium/base/numerics/clamped_math_impl.h new file mode 100644 index 0000000000..303a7e945a --- /dev/null +++ b/security/sandbox/chromium/base/numerics/clamped_math_impl.h @@ -0,0 +1,341 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ +#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <climits> +#include <cmath> +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include "base/numerics/checked_math.h" +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math_shared_impl.h" + +namespace base { +namespace internal { + +template <typename T, + typename std::enable_if<std::is_integral<T>::value && + std::is_signed<T>::value>::type* = nullptr> +constexpr T SaturatedNegWrapper(T value) { + return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported + ? (NegateWrapper(value) != std::numeric_limits<T>::lowest() + ? NegateWrapper(value) + : std::numeric_limits<T>::max()) + : ClampedNegFastOp<T>::Do(value); +} + +template <typename T, + typename std::enable_if<std::is_integral<T>::value && + !std::is_signed<T>::value>::type* = nullptr> +constexpr T SaturatedNegWrapper(T value) { + return T(0); +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> +constexpr T SaturatedNegWrapper(T value) { + return -value; +} + +template <typename T, + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> +constexpr T SaturatedAbsWrapper(T value) { + // The calculation below is a static identity for unsigned types, but for + // signed integer types it provides a non-branching, saturated absolute value. + // This works because SafeUnsignedAbs() returns an unsigned type, which can + // represent the absolute value of all negative numbers of an equal-width + // integer type. The call to IsValueNegative() then detects overflow in the + // special case of numeric_limits<T>::min(), by evaluating the bit pattern as + // a signed integer value. If it is the overflow case, we end up subtracting + // one from the unsigned result, thus saturating to numeric_limits<T>::max(). + return static_cast<T>(SafeUnsignedAbs(value) - + IsValueNegative<T>(SafeUnsignedAbs(value))); +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> +constexpr T SaturatedAbsWrapper(T value) { + return value < 0 ? -value : value; +} + +template <typename T, typename U, class Enable = void> +struct ClampedAddOp {}; + +template <typename T, typename U> +struct ClampedAddOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + if (ClampedAddFastOp<T, U>::is_supported) + return ClampedAddFastOp<T, U>::template Do<V>(x, y); + + static_assert(std::is_same<V, result_type>::value || + IsTypeInRangeForNumericType<U, V>::value, + "The saturation result cannot be determined from the " + "provided types."); + const V saturated = CommonMaxOrMin<V>(IsValueNegative(y)); + V result = {}; + return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result))) + ? result + : saturated; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedSubOp {}; + +template <typename T, typename U> +struct ClampedSubOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + // TODO(jschuh) Make this "constexpr if" once we're C++17. + if (ClampedSubFastOp<T, U>::is_supported) + return ClampedSubFastOp<T, U>::template Do<V>(x, y); + + static_assert(std::is_same<V, result_type>::value || + IsTypeInRangeForNumericType<U, V>::value, + "The saturation result cannot be determined from the " + "provided types."); + const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y)); + V result = {}; + return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result))) + ? result + : saturated; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedMulOp {}; + +template <typename T, typename U> +struct ClampedMulOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + // TODO(jschuh) Make this "constexpr if" once we're C++17. + if (ClampedMulFastOp<T, U>::is_supported) + return ClampedMulFastOp<T, U>::template Do<V>(x, y); + + V result = {}; + const V saturated = + CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); + return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result))) + ? result + : saturated; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedDivOp {}; + +template <typename T, typename U> +struct ClampedDivOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + V result = {}; + if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) + return result; + // Saturation goes to max, min, or NaN (if x is zero). + return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)) + : SaturationDefaultLimits<V>::NaN(); + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedModOp {}; + +template <typename T, typename U> +struct ClampedModOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + V result = {}; + return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result))) + ? result + : x; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedLshOp {}; + +// Left shift. Non-zero values saturate in the direction of the sign. A zero +// shifted by any value always results in zero. +template <typename T, typename U> +struct ClampedLshOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = T; + template <typename V = result_type> + static constexpr V Do(T x, U shift) { + static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); + if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) { + // Shift as unsigned to avoid undefined behavior. + V result = static_cast<V>(as_unsigned(x) << shift); + // If the shift can be reversed, we know it was valid. + if (BASE_NUMERICS_LIKELY(result >> shift == x)) + return result; + } + return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedRshOp {}; + +// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. +template <typename T, typename U> +struct ClampedRshOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = T; + template <typename V = result_type> + static constexpr V Do(T x, U shift) { + static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); + // Signed right shift is odd, because it saturates to -1 or 0. + const V saturated = as_unsigned(V(0)) - IsValueNegative(x); + return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value) + ? saturated_cast<V>(x >> shift) + : saturated; + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedAndOp {}; + +template <typename T, typename U> +struct ClampedAndOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr V Do(T x, U y) { + return static_cast<result_type>(x) & static_cast<result_type>(y); + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedOrOp {}; + +// For simplicity we promote to unsigned integers. +template <typename T, typename U> +struct ClampedOrOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr V Do(T x, U y) { + return static_cast<result_type>(x) | static_cast<result_type>(y); + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedXorOp {}; + +// For simplicity we support only unsigned integers. +template <typename T, typename U> +struct ClampedXorOp<T, + U, + typename std::enable_if<std::is_integral<T>::value && + std::is_integral<U>::value>::type> { + using result_type = typename std::make_unsigned< + typename MaxExponentPromotion<T, U>::type>::type; + template <typename V> + static constexpr V Do(T x, U y) { + return static_cast<result_type>(x) ^ static_cast<result_type>(y); + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedMaxOp {}; + +template <typename T, typename U> +struct ClampedMaxOp< + T, + U, + typename std::enable_if<std::is_arithmetic<T>::value && + std::is_arithmetic<U>::value>::type> { + using result_type = typename MaxExponentPromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) + : saturated_cast<V>(y); + } +}; + +template <typename T, typename U, class Enable = void> +struct ClampedMinOp {}; + +template <typename T, typename U> +struct ClampedMinOp< + T, + U, + typename std::enable_if<std::is_arithmetic<T>::value && + std::is_arithmetic<U>::value>::type> { + using result_type = typename LowestValuePromotion<T, U>::type; + template <typename V = result_type> + static constexpr V Do(T x, U y) { + return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) + : saturated_cast<V>(y); + } +}; + +// This is just boilerplate that wraps the standard floating point arithmetic. +// A macro isn't the nicest solution, but it beats rewriting these repeatedly. +#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ + template <typename T, typename U> \ + struct Clamped##NAME##Op< \ + T, U, \ + typename std::enable_if<std::is_floating_point<T>::value || \ + std::is_floating_point<U>::value>::type> { \ + using result_type = typename MaxExponentPromotion<T, U>::type; \ + template <typename V = result_type> \ + static constexpr V Do(T x, U y) { \ + return saturated_cast<V>(x OP y); \ + } \ + }; + +BASE_FLOAT_ARITHMETIC_OPS(Add, +) +BASE_FLOAT_ARITHMETIC_OPS(Sub, -) +BASE_FLOAT_ARITHMETIC_OPS(Mul, *) +BASE_FLOAT_ARITHMETIC_OPS(Div, /) + +#undef BASE_FLOAT_ARITHMETIC_OPS + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_conversions.h b/security/sandbox/chromium/base/numerics/safe_conversions.h new file mode 100644 index 0000000000..b9636fec42 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_conversions.h @@ -0,0 +1,358 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_H_ + +#include <stddef.h> + +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions_impl.h" + +#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__)) +#include "base/numerics/safe_conversions_arm_impl.h" +#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1) +#else +#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0) +#endif + +#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS +#include <ostream> +#endif + +namespace base { +namespace internal { + +#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS +template <typename Dst, typename Src> +struct SaturateFastAsmOp { + static const bool is_supported = false; + static constexpr Dst Do(Src) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<Dst>(); + } +}; +#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS +#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS + +// The following special case a few specific integer conversions where we can +// eke out better performance than range checking. +template <typename Dst, typename Src, typename Enable = void> +struct IsValueInRangeFastOp { + static const bool is_supported = false; + static constexpr bool Do(Src value) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<bool>(); + } +}; + +// Signed to signed range comparison. +template <typename Dst, typename Src> +struct IsValueInRangeFastOp< + Dst, + Src, + typename std::enable_if< + std::is_integral<Dst>::value && std::is_integral<Src>::value && + std::is_signed<Dst>::value && std::is_signed<Src>::value && + !IsTypeInRangeForNumericType<Dst, Src>::value>::type> { + static const bool is_supported = true; + + static constexpr bool Do(Src value) { + // Just downcast to the smaller type, sign extend it back to the original + // type, and then see if it matches the original value. + return value == static_cast<Dst>(value); + } +}; + +// Signed to unsigned range comparison. +template <typename Dst, typename Src> +struct IsValueInRangeFastOp< + Dst, + Src, + typename std::enable_if< + std::is_integral<Dst>::value && std::is_integral<Src>::value && + !std::is_signed<Dst>::value && std::is_signed<Src>::value && + !IsTypeInRangeForNumericType<Dst, Src>::value>::type> { + static const bool is_supported = true; + + static constexpr bool Do(Src value) { + // We cast a signed as unsigned to overflow negative values to the top, + // then compare against whichever maximum is smaller, as our upper bound. + return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>()); + } +}; + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template <typename Dst, typename Src> +constexpr bool IsValueInRangeForNumericType(Src value) { + using SrcType = typename internal::UnderlyingType<Src>::type; + return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported + ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do( + static_cast<SrcType>(value)) + : internal::DstRangeRelationToSrcRange<Dst>( + static_cast<SrcType>(value)) + .IsValid(); +} + +// checked_cast<> is analogous to static_cast<> for numeric types, +// except that it CHECKs that the specified numeric conversion will not +// overflow or underflow. NaN source will always trigger a CHECK. +template <typename Dst, + class CheckHandler = internal::CheckOnFailure, + typename Src> +constexpr Dst checked_cast(Src value) { + // This throws a compile-time error on evaluating the constexpr if it can be + // determined at compile-time as failing, otherwise it will CHECK at runtime. + using SrcType = typename internal::UnderlyingType<Src>::type; + return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value))) + ? static_cast<Dst>(static_cast<SrcType>(value)) + : CheckHandler::template HandleFailure<Dst>(); +} + +// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. +// You may provide your own limits (e.g. to saturated_cast) so long as you +// implement all of the static constexpr member functions in the class below. +template <typename T> +struct SaturationDefaultLimits : public std::numeric_limits<T> { + static constexpr T NaN() { + return std::numeric_limits<T>::has_quiet_NaN + ? std::numeric_limits<T>::quiet_NaN() + : T(); + } + using std::numeric_limits<T>::max; + static constexpr T Overflow() { + return std::numeric_limits<T>::has_infinity + ? std::numeric_limits<T>::infinity() + : std::numeric_limits<T>::max(); + } + using std::numeric_limits<T>::lowest; + static constexpr T Underflow() { + return std::numeric_limits<T>::has_infinity + ? std::numeric_limits<T>::infinity() * -1 + : std::numeric_limits<T>::lowest(); + } +}; + +template <typename Dst, template <typename> class S, typename Src> +constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { + // For some reason clang generates much better code when the branch is + // structured exactly this way, rather than a sequence of checks. + return !constraint.IsOverflowFlagSet() + ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) + : S<Dst>::Underflow()) + // Skip this check for integral Src, which cannot be NaN. + : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() + ? S<Dst>::Overflow() + : S<Dst>::NaN()); +} + +// We can reduce the number of conditions and get slightly better performance +// for normal signed and unsigned integer ranges. And in the specific case of +// Arm, we can use the optimized saturation instructions. +template <typename Dst, typename Src, typename Enable = void> +struct SaturateFastOp { + static const bool is_supported = false; + static constexpr Dst Do(Src value) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<Dst>(); + } +}; + +template <typename Dst, typename Src> +struct SaturateFastOp< + Dst, + Src, + typename std::enable_if<std::is_integral<Src>::value && + std::is_integral<Dst>::value && + SaturateFastAsmOp<Dst, Src>::is_supported>::type> { + static const bool is_supported = true; + static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); } +}; + +template <typename Dst, typename Src> +struct SaturateFastOp< + Dst, + Src, + typename std::enable_if<std::is_integral<Src>::value && + std::is_integral<Dst>::value && + !SaturateFastAsmOp<Dst, Src>::is_supported>::type> { + static const bool is_supported = true; + static Dst Do(Src value) { + // The exact order of the following is structured to hit the correct + // optimization heuristics across compilers. Do not change without + // checking the emitted code. + Dst saturated = CommonMaxOrMin<Dst, Src>( + IsMaxInRangeForNumericType<Dst, Src>() || + (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value))); + return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value)) + ? static_cast<Dst>(value) + : saturated; + } +}; + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate by default rather than +// overflow or underflow, and NaN assignment to an integral will return 0. +// All boundary condition behaviors can be overriden with a custom handler. +template <typename Dst, + template <typename> class SaturationHandler = SaturationDefaultLimits, + typename Src> +constexpr Dst saturated_cast(Src value) { + using SrcType = typename UnderlyingType<Src>::type; + return !IsCompileTimeConstant(value) && + SaturateFastOp<Dst, SrcType>::is_supported && + std::is_same<SaturationHandler<Dst>, + SaturationDefaultLimits<Dst>>::value + ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value)) + : saturated_cast_impl<Dst, SaturationHandler, SrcType>( + static_cast<SrcType>(value), + DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>( + static_cast<SrcType>(value))); +} + +// strict_cast<> is analogous to static_cast<> for numeric types, except that +// it will cause a compile failure if the destination type is not large enough +// to contain any value in the source type. It performs no runtime checking. +template <typename Dst, typename Src> +constexpr Dst strict_cast(Src value) { + using SrcType = typename UnderlyingType<Src>::type; + static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); + static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); + + // If you got here from a compiler error, it's because you tried to assign + // from a source type to a destination type that has insufficient range. + // The solution may be to change the destination type you're assigning to, + // and use one large enough to represent the source. + // Alternatively, you may be better served with the checked_cast<> or + // saturated_cast<> template functions for your particular use case. + static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value == + NUMERIC_RANGE_CONTAINED, + "The source type is out of range for the destination type. " + "Please see strict_cast<> comments for more information."); + + return static_cast<Dst>(static_cast<SrcType>(value)); +} + +// Some wrappers to statically check that a type is in range. +template <typename Dst, typename Src, class Enable = void> +struct IsNumericRangeContained { + static const bool value = false; +}; + +template <typename Dst, typename Src> +struct IsNumericRangeContained< + Dst, + Src, + typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value && + ArithmeticOrUnderlyingEnum<Src>::value>::type> { + static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == + NUMERIC_RANGE_CONTAINED; +}; + +// StrictNumeric implements compile time range checking between numeric types by +// wrapping assignment operations in a strict_cast. This class is intended to be +// used for function arguments and return types, to ensure the destination type +// can always contain the source type. This is essentially the same as enforcing +// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied +// incrementally at API boundaries, making it easier to convert code so that it +// compiles cleanly with truncation warnings enabled. +// This template should introduce no runtime overhead, but it also provides no +// runtime checking of any of the associated mathematical operations. Use +// CheckedNumeric for runtime range checks of the actual value being assigned. +template <typename T> +class StrictNumeric { + public: + using type = T; + + constexpr StrictNumeric() : value_(0) {} + + // Copy constructor. + template <typename Src> + constexpr StrictNumeric(const StrictNumeric<Src>& rhs) + : value_(strict_cast<T>(rhs.value_)) {} + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to StrictNumerics to make them easier to use. + template <typename Src> + constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit) + : value_(strict_cast<T>(value)) {} + + // If you got here from a compiler error, it's because you tried to assign + // from a source type to a destination type that has insufficient range. + // The solution may be to change the destination type you're assigning to, + // and use one large enough to represent the source. + // If you're assigning from a CheckedNumeric<> class, you may be able to use + // the AssignIfValid() member function, specify a narrower destination type to + // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one + // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)). + // If you've encountered an _ambiguous overload_ you can use a static_cast<> + // to explicitly cast the result to the destination type. + // If none of that works, you may be better served with the checked_cast<> or + // saturated_cast<> template functions for your particular use case. + template <typename Dst, + typename std::enable_if< + IsNumericRangeContained<Dst, T>::value>::type* = nullptr> + constexpr operator Dst() const { + return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); + } + + private: + const T value_; +}; + +// Convience wrapper returns a StrictNumeric from the provided arithmetic type. +template <typename T> +constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum( + const T value) { + return value; +} + +#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS +// Overload the ostream output operator to make logging work nicely. +template <typename T> +std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { + os << static_cast<T>(value); + return os; +} +#endif + +#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ + template <typename L, typename R, \ + typename std::enable_if< \ + internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \ + constexpr bool operator OP(const L lhs, const R rhs) { \ + return SafeCompare<NAME, typename UnderlyingType<L>::type, \ + typename UnderlyingType<R>::type>(lhs, rhs); \ + } + +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=) + +} // namespace internal + +using internal::as_signed; +using internal::as_unsigned; +using internal::checked_cast; +using internal::strict_cast; +using internal::saturated_cast; +using internal::SafeUnsignedAbs; +using internal::StrictNumeric; +using internal::MakeStrictNum; +using internal::IsValueInRangeForNumericType; +using internal::IsTypeInRangeForNumericType; +using internal::IsValueNegative; + +// Explicitly make a shorter size_t alias for convenience. +using SizeT = StrictNumeric<size_t>; + +} // namespace base + +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h b/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h new file mode 100644 index 0000000000..cf31072b9b --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h @@ -0,0 +1,51 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_ + +#include <cassert> +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions_impl.h" + +namespace base { +namespace internal { + +// Fast saturation to a destination type. +template <typename Dst, typename Src> +struct SaturateFastAsmOp { + static constexpr bool is_supported = + std::is_signed<Src>::value && std::is_integral<Dst>::value && + std::is_integral<Src>::value && + IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value && + IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value && + !IsTypeInRangeForNumericType<Dst, Src>::value; + + __attribute__((always_inline)) static Dst Do(Src value) { + int32_t src = value; + typename std::conditional<std::is_signed<Dst>::value, int32_t, + uint32_t>::type result; + if (std::is_signed<Dst>::value) { + asm("ssat %[dst], %[shift], %[src]" + : [dst] "=r"(result) + : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32 + ? IntegerBitsPlusSign<Dst>::value + : 32)); + } else { + asm("usat %[dst], %[shift], %[src]" + : [dst] "=r"(result) + : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32 + ? IntegerBitsPlusSign<Dst>::value + : 31)); + } + return static_cast<Dst>(result); + } +}; + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_conversions_impl.h b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h new file mode 100644 index 0000000000..7c5ca68c3b --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h @@ -0,0 +1,851 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ + +#include <stdint.h> + +#include <limits> +#include <type_traits> + +#if defined(__GNUC__) || defined(__clang__) +#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) +#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define BASE_NUMERICS_LIKELY(x) (x) +#define BASE_NUMERICS_UNLIKELY(x) (x) +#endif + +namespace base { +namespace internal { + +// The std library doesn't provide a binary max_exponent for integers, however +// we can compute an analog using std::numeric_limits<>::digits. +template <typename NumericType> +struct MaxExponent { + static const int value = std::is_floating_point<NumericType>::value + ? std::numeric_limits<NumericType>::max_exponent + : std::numeric_limits<NumericType>::digits + 1; +}; + +// The number of bits (including the sign) in an integer. Eliminates sizeof +// hacks. +template <typename NumericType> +struct IntegerBitsPlusSign { + static const int value = std::numeric_limits<NumericType>::digits + + std::is_signed<NumericType>::value; +}; + +// Helper templates for integer manipulations. + +template <typename Integer> +struct PositionOfSignBit { + static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; +}; + +// Determines if a numeric value is negative without throwing compiler +// warnings on: unsigned(value) < 0. +template <typename T, + typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> +constexpr bool IsValueNegative(T value) { + static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); + return value < 0; +} + +template <typename T, + typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> +constexpr bool IsValueNegative(T) { + static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); + return false; +} + +// This performs a fast negation, returning a signed value. It works on unsigned +// arguments, but probably doesn't do what you want for any unsigned value +// larger than max / 2 + 1 (i.e. signed min cast to unsigned). +template <typename T> +constexpr typename std::make_signed<T>::type ConditionalNegate( + T x, + bool is_negative) { + static_assert(std::is_integral<T>::value, "Type must be integral"); + using SignedT = typename std::make_signed<T>::type; + using UnsignedT = typename std::make_unsigned<T>::type; + return static_cast<SignedT>( + (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative); +} + +// This performs a safe, absolute value via unsigned overflow. +template <typename T> +constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { + static_assert(std::is_integral<T>::value, "Type must be integral"); + using UnsignedT = typename std::make_unsigned<T>::type; + return IsValueNegative(value) + ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) + : static_cast<UnsignedT>(value); +} + +// This allows us to switch paths on known compile-time constants. +#if defined(__clang__) || defined(__GNUC__) +constexpr bool CanDetectCompileTimeConstant() { + return true; +} +template <typename T> +constexpr bool IsCompileTimeConstant(const T v) { + return __builtin_constant_p(v); +} +#else +constexpr bool CanDetectCompileTimeConstant() { + return false; +} +template <typename T> +constexpr bool IsCompileTimeConstant(const T) { + return false; +} +#endif +template <typename T> +constexpr bool MustTreatAsConstexpr(const T v) { + // Either we can't detect a compile-time constant, and must always use the + // constexpr path, or we know we have a compile-time constant. + return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v); +} + +// Forces a crash, like a CHECK(false). Used for numeric boundary errors. +// Also used in a constexpr template to trigger a compilation failure on +// an error condition. +struct CheckOnFailure { + template <typename T> + static T HandleFailure() { +#if defined(_MSC_VER) + __debugbreak(); +#elif defined(__GNUC__) || defined(__clang__) + __builtin_trap(); +#else + ((void)(*(volatile char*)0 = 0)); +#endif + return T(); + } +}; + +enum IntegerRepresentation { + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED +}; + +// A range for a given nunmeric Src type is contained for a given numeric Dst +// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and +// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. +// We implement this as template specializations rather than simple static +// comparisons to ensure type correctness in our comparisons. +enum NumericRangeRepresentation { + NUMERIC_RANGE_NOT_CONTAINED, + NUMERIC_RANGE_CONTAINED +}; + +// Helper templates to statically determine if our destination type can contain +// maximum and minimum values represented by the source type. + +template <typename Dst, + typename Src, + IntegerRepresentation DstSign = std::is_signed<Dst>::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::is_signed<Src>::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED> +struct StaticDstRangeRelationToSrcRange; + +// Same sign: Dst is guaranteed to contain Src only if its range is equal or +// larger. +template <typename Dst, typename Src, IntegerRepresentation Sign> +struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value >= MaxExponent<Src>::value + ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Unsigned to signed: Dst is guaranteed to contain source only if its range is +// larger. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED> { + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value > MaxExponent<Src>::value + ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Signed to unsigned: Dst cannot be statically determined to contain Src. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED> { + static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; +}; + +// This class wraps the range constraints as separate booleans so the compiler +// can identify constants and eliminate unused code paths. +class RangeCheck { + public: + constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) + : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} + constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {} + constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } + constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } + constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } + constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } + constexpr bool IsOverflowFlagSet() const { return is_overflow_; } + constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } + constexpr bool operator==(const RangeCheck rhs) const { + return is_underflow_ == rhs.is_underflow_ && + is_overflow_ == rhs.is_overflow_; + } + constexpr bool operator!=(const RangeCheck rhs) const { + return !(*this == rhs); + } + + private: + // Do not change the order of these member variables. The integral conversion + // optimization depends on this exact order. + const bool is_underflow_; + const bool is_overflow_; +}; + +// The following helper template addresses a corner case in range checks for +// conversion from a floating-point type to an integral type of smaller range +// but larger precision (e.g. float -> unsigned). The problem is as follows: +// 1. Integral maximum is always one less than a power of two, so it must be +// truncated to fit the mantissa of the floating point. The direction of +// rounding is implementation defined, but by default it's always IEEE +// floats, which round to nearest and thus result in a value of larger +// magnitude than the integral value. +// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX +// // is 4294967295u. +// 2. If the floating point value is equal to the promoted integral maximum +// value, a range check will erroneously pass. +// Example: (4294967296f <= 4294967295u) // This is true due to a precision +// // loss in rounding up to float. +// 3. When the floating point value is then converted to an integral, the +// resulting value is out of range for the target integral type and +// thus is implementation defined. +// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. +// To fix this bug we manually truncate the maximum value when the destination +// type is an integral of larger precision than the source floating-point type, +// such that the resulting maximum is represented exactly as a floating point. +template <typename Dst, typename Src, template <typename> class Bounds> +struct NarrowingRange { + using SrcLimits = std::numeric_limits<Src>; + using DstLimits = typename std::numeric_limits<Dst>; + + // Computes the mask required to make an accurate comparison between types. + static const int kShift = + (MaxExponent<Src>::value > MaxExponent<Dst>::value && + SrcLimits::digits < DstLimits::digits) + ? (DstLimits::digits - SrcLimits::digits) + : 0; + template < + typename T, + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> + + // Masks out the integer bits that are beyond the precision of the + // intermediate type used for comparison. + static constexpr T Adjust(T value) { + static_assert(std::is_same<T, Dst>::value, ""); + static_assert(kShift < DstLimits::digits, ""); + return static_cast<T>( + ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), + IsValueNegative(value))); + } + + template <typename T, + typename std::enable_if<std::is_floating_point<T>::value>::type* = + nullptr> + static constexpr T Adjust(T value) { + static_assert(std::is_same<T, Dst>::value, ""); + static_assert(kShift == 0, ""); + return value; + } + + static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } + static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } +}; + +template <typename Dst, + typename Src, + template <typename> class Bounds, + IntegerRepresentation DstSign = std::is_signed<Dst>::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::is_signed<Src>::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + NumericRangeRepresentation DstRange = + StaticDstRangeRelationToSrcRange<Dst, Src>::value> +struct DstRangeRelationToSrcRangeImpl; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Same sign narrowing: The range is contained for normal limits. +template <typename Dst, + typename Src, + template <typename> class Bounds, + IntegerRepresentation DstSign, + IntegerRepresentation SrcSign> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + Bounds, + DstSign, + SrcSign, + NUMERIC_RANGE_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using SrcLimits = std::numeric_limits<Src>; + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeCheck( + static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || + static_cast<Dst>(value) >= DstLimits::lowest(), + static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || + static_cast<Dst>(value) <= DstLimits::max()); + } +}; + +// Signed to signed narrowing: Both the upper and lower boundaries may be +// exceeded for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + Bounds, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); + } +}; + +// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for +// standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + Bounds, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + return RangeCheck( + DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), + value <= DstLimits::max()); + } +}; + +// Unsigned to signed: Only the upper bound can be exceeded for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + Bounds, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + using Promotion = decltype(Src() + Dst()); + return RangeCheck(DstLimits::lowest() <= Dst(0) || + static_cast<Promotion>(value) >= + static_cast<Promotion>(DstLimits::lowest()), + static_cast<Promotion>(value) <= + static_cast<Promotion>(DstLimits::max())); + } +}; + +// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, +// and any negative value exceeds the lower boundary for standard limits. +template <typename Dst, typename Src, template <typename> class Bounds> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + Bounds, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using SrcLimits = std::numeric_limits<Src>; + using DstLimits = NarrowingRange<Dst, Src, Bounds>; + using Promotion = decltype(Src() + Dst()); + return RangeCheck( + value >= Src(0) && (DstLimits::lowest() == 0 || + static_cast<Dst>(value) >= DstLimits::lowest()), + static_cast<Promotion>(SrcLimits::max()) <= + static_cast<Promotion>(DstLimits::max()) || + static_cast<Promotion>(value) <= + static_cast<Promotion>(DstLimits::max())); + } +}; + +// Simple wrapper for statically checking if a type's range is contained. +template <typename Dst, typename Src> +struct IsTypeInRangeForNumericType { + static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == + NUMERIC_RANGE_CONTAINED; +}; + +template <typename Dst, + template <typename> class Bounds = std::numeric_limits, + typename Src> +constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { + static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); + static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); + static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); + return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); +} + +// Integer promotion templates used by the portable checked integer arithmetic. +template <size_t Size, bool IsSigned> +struct IntegerForDigitsAndSign; + +#define INTEGER_FOR_DIGITS_AND_SIGN(I) \ + template <> \ + struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ + std::is_signed<I>::value> { \ + using type = I; \ + } + +INTEGER_FOR_DIGITS_AND_SIGN(int8_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); +INTEGER_FOR_DIGITS_AND_SIGN(int16_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); +INTEGER_FOR_DIGITS_AND_SIGN(int32_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); +INTEGER_FOR_DIGITS_AND_SIGN(int64_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); +#undef INTEGER_FOR_DIGITS_AND_SIGN + +// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to +// support 128-bit math, then the ArithmeticPromotion template below will need +// to be updated (or more likely replaced with a decltype expression). +static_assert(IntegerBitsPlusSign<intmax_t>::value == 64, + "Max integer size not supported for this toolchain."); + +template <typename Integer, bool IsSigned = std::is_signed<Integer>::value> +struct TwiceWiderInteger { + using type = + typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, + IsSigned>::type; +}; + +enum ArithmeticPromotionCategory { + LEFT_PROMOTION, // Use the type of the left-hand argument. + RIGHT_PROMOTION // Use the type of the right-hand argument. +}; + +// Determines the type that can represent the largest positive value. +template <typename Lhs, + typename Rhs, + ArithmeticPromotionCategory Promotion = + (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) + ? LEFT_PROMOTION + : RIGHT_PROMOTION> +struct MaxExponentPromotion; + +template <typename Lhs, typename Rhs> +struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { + using type = Lhs; +}; + +template <typename Lhs, typename Rhs> +struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { + using type = Rhs; +}; + +// Determines the type that can represent the lowest arithmetic value. +template <typename Lhs, + typename Rhs, + ArithmeticPromotionCategory Promotion = + std::is_signed<Lhs>::value + ? (std::is_signed<Rhs>::value + ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value + ? LEFT_PROMOTION + : RIGHT_PROMOTION) + : LEFT_PROMOTION) + : (std::is_signed<Rhs>::value + ? RIGHT_PROMOTION + : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value + ? LEFT_PROMOTION + : RIGHT_PROMOTION))> +struct LowestValuePromotion; + +template <typename Lhs, typename Rhs> +struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { + using type = Lhs; +}; + +template <typename Lhs, typename Rhs> +struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { + using type = Rhs; +}; + +// Determines the type that is best able to represent an arithmetic result. +template < + typename Lhs, + typename Rhs = Lhs, + bool is_intmax_type = + std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&& + IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: + value == IntegerBitsPlusSign<intmax_t>::value, + bool is_max_exponent = + StaticDstRangeRelationToSrcRange< + typename MaxExponentPromotion<Lhs, Rhs>::type, + Lhs>::value == + NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< + typename MaxExponentPromotion<Lhs, Rhs>::type, + Rhs>::value == NUMERIC_RANGE_CONTAINED> +struct BigEnoughPromotion; + +// The side with the max exponent is big enough. +template <typename Lhs, typename Rhs, bool is_intmax_type> +struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { + using type = typename MaxExponentPromotion<Lhs, Rhs>::type; + static const bool is_contained = true; +}; + +// We can use a twice wider type to fit. +template <typename Lhs, typename Rhs> +struct BigEnoughPromotion<Lhs, Rhs, false, false> { + using type = + typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, + std::is_signed<Lhs>::value || + std::is_signed<Rhs>::value>::type; + static const bool is_contained = true; +}; + +// No type is large enough. +template <typename Lhs, typename Rhs> +struct BigEnoughPromotion<Lhs, Rhs, true, false> { + using type = typename MaxExponentPromotion<Lhs, Rhs>::type; + static const bool is_contained = false; +}; + +// We can statically check if operations on the provided types can wrap, so we +// can skip the checked operations if they're not needed. So, for an integer we +// care if the destination type preserves the sign and is twice the width of +// the source. +template <typename T, typename Lhs, typename Rhs = Lhs> +struct IsIntegerArithmeticSafe { + static const bool value = + !std::is_floating_point<T>::value && + !std::is_floating_point<Lhs>::value && + !std::is_floating_point<Rhs>::value && + std::is_signed<T>::value >= std::is_signed<Lhs>::value && + IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && + std::is_signed<T>::value >= std::is_signed<Rhs>::value && + IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); +}; + +// Promotes to a type that can represent any possible result of a binary +// arithmetic operation with the source types. +template <typename Lhs, + typename Rhs, + bool is_promotion_possible = IsIntegerArithmeticSafe< + typename std::conditional<std::is_signed<Lhs>::value || + std::is_signed<Rhs>::value, + intmax_t, + uintmax_t>::type, + typename MaxExponentPromotion<Lhs, Rhs>::type>::value> +struct FastIntegerArithmeticPromotion; + +template <typename Lhs, typename Rhs> +struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> { + using type = + typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, + std::is_signed<Lhs>::value || + std::is_signed<Rhs>::value>::type; + static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, ""); + static const bool is_contained = true; +}; + +template <typename Lhs, typename Rhs> +struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> { + using type = typename BigEnoughPromotion<Lhs, Rhs>::type; + static const bool is_contained = false; +}; + +// Extracts the underlying type from an enum. +template <typename T, bool is_enum = std::is_enum<T>::value> +struct ArithmeticOrUnderlyingEnum; + +template <typename T> +struct ArithmeticOrUnderlyingEnum<T, true> { + using type = typename std::underlying_type<T>::type; + static const bool value = std::is_arithmetic<type>::value; +}; + +template <typename T> +struct ArithmeticOrUnderlyingEnum<T, false> { + using type = T; + static const bool value = std::is_arithmetic<type>::value; +}; + +// The following are helper templates used in the CheckedNumeric class. +template <typename T> +class CheckedNumeric; + +template <typename T> +class ClampedNumeric; + +template <typename T> +class StrictNumeric; + +// Used to treat CheckedNumeric and arithmetic underlying types the same. +template <typename T> +struct UnderlyingType { + using type = typename ArithmeticOrUnderlyingEnum<T>::type; + static const bool is_numeric = std::is_arithmetic<type>::value; + static const bool is_checked = false; + static const bool is_clamped = false; + static const bool is_strict = false; +}; + +template <typename T> +struct UnderlyingType<CheckedNumeric<T>> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = true; + static const bool is_clamped = false; + static const bool is_strict = false; +}; + +template <typename T> +struct UnderlyingType<ClampedNumeric<T>> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = false; + static const bool is_clamped = true; + static const bool is_strict = false; +}; + +template <typename T> +struct UnderlyingType<StrictNumeric<T>> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = false; + static const bool is_clamped = false; + static const bool is_strict = true; +}; + +template <typename L, typename R> +struct IsCheckedOp { + static const bool value = + UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && + (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); +}; + +template <typename L, typename R> +struct IsClampedOp { + static const bool value = + UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && + (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) && + !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); +}; + +template <typename L, typename R> +struct IsStrictOp { + static const bool value = + UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && + (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) && + !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) && + !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped); +}; + +// as_signed<> returns the supplied integral value (or integral castable +// Numeric template) cast as a signed integral of equivalent precision. +// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) +template <typename Src> +constexpr typename std::make_signed< + typename base::internal::UnderlyingType<Src>::type>::type +as_signed(const Src value) { + static_assert(std::is_integral<decltype(as_signed(value))>::value, + "Argument must be a signed or unsigned integer type."); + return static_cast<decltype(as_signed(value))>(value); +} + +// as_unsigned<> returns the supplied integral value (or integral castable +// Numeric template) cast as an unsigned integral of equivalent precision. +// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) +template <typename Src> +constexpr typename std::make_unsigned< + typename base::internal::UnderlyingType<Src>::type>::type +as_unsigned(const Src value) { + static_assert(std::is_integral<decltype(as_unsigned(value))>::value, + "Argument must be a signed or unsigned integer type."); + return static_cast<decltype(as_unsigned(value))>(value); +} + +template <typename L, typename R> +constexpr bool IsLessImpl(const L lhs, + const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsUnderflow() || r_range.IsOverflow() || + (l_range == r_range && + static_cast<decltype(lhs + rhs)>(lhs) < + static_cast<decltype(lhs + rhs)>(rhs)); +} + +template <typename L, typename R> +struct IsLess { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), + DstRangeRelationToSrcRange<L>(rhs)); + } +}; + +template <typename L, typename R> +constexpr bool IsLessOrEqualImpl(const L lhs, + const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsUnderflow() || r_range.IsOverflow() || + (l_range == r_range && + static_cast<decltype(lhs + rhs)>(lhs) <= + static_cast<decltype(lhs + rhs)>(rhs)); +} + +template <typename L, typename R> +struct IsLessOrEqual { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), + DstRangeRelationToSrcRange<L>(rhs)); + } +}; + +template <typename L, typename R> +constexpr bool IsGreaterImpl(const L lhs, + const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsOverflow() || r_range.IsUnderflow() || + (l_range == r_range && + static_cast<decltype(lhs + rhs)>(lhs) > + static_cast<decltype(lhs + rhs)>(rhs)); +} + +template <typename L, typename R> +struct IsGreater { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), + DstRangeRelationToSrcRange<L>(rhs)); + } +}; + +template <typename L, typename R> +constexpr bool IsGreaterOrEqualImpl(const L lhs, + const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsOverflow() || r_range.IsUnderflow() || + (l_range == r_range && + static_cast<decltype(lhs + rhs)>(lhs) >= + static_cast<decltype(lhs + rhs)>(rhs)); +} + +template <typename L, typename R> +struct IsGreaterOrEqual { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), + DstRangeRelationToSrcRange<L>(rhs)); + } +}; + +template <typename L, typename R> +struct IsEqual { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return DstRangeRelationToSrcRange<R>(lhs) == + DstRangeRelationToSrcRange<L>(rhs) && + static_cast<decltype(lhs + rhs)>(lhs) == + static_cast<decltype(lhs + rhs)>(rhs); + } +}; + +template <typename L, typename R> +struct IsNotEqual { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return DstRangeRelationToSrcRange<R>(lhs) != + DstRangeRelationToSrcRange<L>(rhs) || + static_cast<decltype(lhs + rhs)>(lhs) != + static_cast<decltype(lhs + rhs)>(rhs); + } +}; + +// These perform the actual math operations on the CheckedNumerics. +// Binary arithmetic operations. +template <template <typename, typename> class C, typename L, typename R> +constexpr bool SafeCompare(const L lhs, const R rhs) { + static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, + "Types must be numeric."); + using Promotion = BigEnoughPromotion<L, R>; + using BigType = typename Promotion::type; + return Promotion::is_contained + // Force to a larger type for speed if both are contained. + ? C<BigType, BigType>::Test( + static_cast<BigType>(static_cast<L>(lhs)), + static_cast<BigType>(static_cast<R>(rhs))) + // Let the template functions figure it out for mixed types. + : C<L, R>::Test(lhs, rhs); +} + +template <typename Dst, typename Src> +constexpr bool IsMaxInRangeForNumericType() { + return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), + std::numeric_limits<Src>::max()); +} + +template <typename Dst, typename Src> +constexpr bool IsMinInRangeForNumericType() { + return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), + std::numeric_limits<Src>::lowest()); +} + +template <typename Dst, typename Src> +constexpr Dst CommonMax() { + return !IsMaxInRangeForNumericType<Dst, Src>() + ? Dst(std::numeric_limits<Dst>::max()) + : Dst(std::numeric_limits<Src>::max()); +} + +template <typename Dst, typename Src> +constexpr Dst CommonMin() { + return !IsMinInRangeForNumericType<Dst, Src>() + ? Dst(std::numeric_limits<Dst>::lowest()) + : Dst(std::numeric_limits<Src>::lowest()); +} + +// This is a wrapper to generate return the max or min for a supplied type. +// If the argument is false, the returned value is the maximum. If true the +// returned value is the minimum. +template <typename Dst, typename Src = Dst> +constexpr Dst CommonMaxOrMin(bool is_min) { + return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>(); +} + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math.h b/security/sandbox/chromium/base/numerics/safe_math.h new file mode 100644 index 0000000000..e30be901f9 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math.h @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_MATH_H_ +#define BASE_NUMERICS_SAFE_MATH_H_ + +#include "base/numerics/checked_math.h" +#include "base/numerics/clamped_math.h" +#include "base/numerics/safe_conversions.h" + +#endif // BASE_NUMERICS_SAFE_MATH_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h b/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h new file mode 100644 index 0000000000..ff86bd0b73 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h @@ -0,0 +1,122 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ +#define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ + +#include <cassert> +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions.h" + +namespace base { +namespace internal { + +template <typename T, typename U> +struct CheckedMulFastAsmOp { + static const bool is_supported = + FastIntegerArithmeticPromotion<T, U>::is_contained; + + // The following is much more efficient than the Clang and GCC builtins for + // performing overflow-checked multiplication when a twice wider type is + // available. The below compiles down to 2-3 instructions, depending on the + // width of the types in use. + // As an example, an int32_t multiply compiles to: + // smull r0, r1, r0, r1 + // cmp r1, r1, asr #31 + // And an int16_t multiply compiles to: + // smulbb r1, r1, r0 + // asr r2, r1, #16 + // cmp r2, r1, asr #15 + template <typename V> + __attribute__((always_inline)) static bool Do(T x, U y, V* result) { + using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; + Promotion presult; + + presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); + *result = static_cast<V>(presult); + return IsValueInRangeForNumericType<V>(presult); + } +}; + +template <typename T, typename U> +struct ClampedAddFastAsmOp { + static const bool is_supported = + BigEnoughPromotion<T, U>::is_contained && + IsTypeInRangeForNumericType< + int32_t, + typename BigEnoughPromotion<T, U>::type>::value; + + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + // This will get promoted to an int, so let the compiler do whatever is + // clever and rely on the saturated cast to bounds check. + if (IsIntegerArithmeticSafe<int, T, U>::value) + return saturated_cast<V>(x + y); + + int32_t result; + int32_t x_i32 = checked_cast<int32_t>(x); + int32_t y_i32 = checked_cast<int32_t>(y); + + asm("qadd %[result], %[first], %[second]" + : [result] "=r"(result) + : [first] "r"(x_i32), [second] "r"(y_i32)); + return saturated_cast<V>(result); + } +}; + +template <typename T, typename U> +struct ClampedSubFastAsmOp { + static const bool is_supported = + BigEnoughPromotion<T, U>::is_contained && + IsTypeInRangeForNumericType< + int32_t, + typename BigEnoughPromotion<T, U>::type>::value; + + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + // This will get promoted to an int, so let the compiler do whatever is + // clever and rely on the saturated cast to bounds check. + if (IsIntegerArithmeticSafe<int, T, U>::value) + return saturated_cast<V>(x - y); + + int32_t result; + int32_t x_i32 = checked_cast<int32_t>(x); + int32_t y_i32 = checked_cast<int32_t>(y); + + asm("qsub %[result], %[first], %[second]" + : [result] "=r"(result) + : [first] "r"(x_i32), [second] "r"(y_i32)); + return saturated_cast<V>(result); + } +}; + +template <typename T, typename U> +struct ClampedMulFastAsmOp { + static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported; + + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + // Use the CheckedMulFastAsmOp for full-width 32-bit values, because + // it's fewer instructions than promoting and then saturating. + if (!IsIntegerArithmeticSafe<int32_t, T, U>::value && + !IsIntegerArithmeticSafe<uint32_t, T, U>::value) { + V result; + if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result)) + return result; + return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); + } + + assert((FastIntegerArithmeticPromotion<T, U>::is_contained)); + using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; + return saturated_cast<V>(static_cast<Promotion>(x) * + static_cast<Promotion>(y)); + } +}; + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h b/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h new file mode 100644 index 0000000000..1760338b08 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h @@ -0,0 +1,157 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_ +#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_ + +#include <cassert> +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions.h" + +#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__)) +#include "base/numerics/safe_math_arm_impl.h" +#define BASE_HAS_ASSEMBLER_SAFE_MATH (1) +#else +#define BASE_HAS_ASSEMBLER_SAFE_MATH (0) +#endif + +namespace base { +namespace internal { + +// These are the non-functioning boilerplate implementations of the optimized +// safe math routines. +#if !BASE_HAS_ASSEMBLER_SAFE_MATH +template <typename T, typename U> +struct CheckedMulFastAsmOp { + static const bool is_supported = false; + template <typename V> + static constexpr bool Do(T, U, V*) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<bool>(); + } +}; + +template <typename T, typename U> +struct ClampedAddFastAsmOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; + +template <typename T, typename U> +struct ClampedSubFastAsmOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; + +template <typename T, typename U> +struct ClampedMulFastAsmOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; +#endif // BASE_HAS_ASSEMBLER_SAFE_MATH +#undef BASE_HAS_ASSEMBLER_SAFE_MATH + +template <typename T, typename U> +struct CheckedAddFastOp { + static const bool is_supported = true; + template <typename V> + __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { + return !__builtin_add_overflow(x, y, result); + } +}; + +template <typename T, typename U> +struct CheckedSubFastOp { + static const bool is_supported = true; + template <typename V> + __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { + return !__builtin_sub_overflow(x, y, result); + } +}; + +template <typename T, typename U> +struct CheckedMulFastOp { +#if defined(__clang__) + // TODO(jschuh): Get the Clang runtime library issues sorted out so we can + // support full-width, mixed-sign multiply builtins. + // https://crbug.com/613003 + // We can support intptr_t, uintptr_t, or a smaller common type. + static const bool is_supported = + (IsTypeInRangeForNumericType<intptr_t, T>::value && + IsTypeInRangeForNumericType<intptr_t, U>::value) || + (IsTypeInRangeForNumericType<uintptr_t, T>::value && + IsTypeInRangeForNumericType<uintptr_t, U>::value); +#else + static const bool is_supported = true; +#endif + template <typename V> + __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { + return CheckedMulFastAsmOp<T, U>::is_supported + ? CheckedMulFastAsmOp<T, U>::Do(x, y, result) + : !__builtin_mul_overflow(x, y, result); + } +}; + +template <typename T, typename U> +struct ClampedAddFastOp { + static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported; + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y); + } +}; + +template <typename T, typename U> +struct ClampedSubFastOp { + static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported; + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y); + } +}; + +template <typename T, typename U> +struct ClampedMulFastOp { + static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported; + template <typename V> + __attribute__((always_inline)) static V Do(T x, U y) { + return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y); + } +}; + +template <typename T> +struct ClampedNegFastOp { + static const bool is_supported = std::is_signed<T>::value; + __attribute__((always_inline)) static T Do(T value) { + // Use this when there is no assembler path available. + if (!ClampedSubFastAsmOp<T, T>::is_supported) { + T result; + return !__builtin_sub_overflow(T(0), value, &result) + ? result + : std::numeric_limits<T>::max(); + } + + // Fallback to the normal subtraction path. + return ClampedSubFastOp<T, T>::template Do<T>(T(0), value); + } +}; + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h b/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h new file mode 100644 index 0000000000..3556b1ea81 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h @@ -0,0 +1,240 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ +#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <cassert> +#include <climits> +#include <cmath> +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include "base/numerics/safe_conversions.h" + +#ifdef __asmjs__ +// Optimized safe math instructions are incompatible with asmjs. +#define BASE_HAS_OPTIMIZED_SAFE_MATH (0) +// Where available use builtin math overflow support on Clang and GCC. +#elif !defined(__native_client__) && \ + ((defined(__clang__) && \ + ((__clang_major__ > 3) || \ + (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ + (defined(__GNUC__) && __GNUC__ >= 5)) +#include "base/numerics/safe_math_clang_gcc_impl.h" +#define BASE_HAS_OPTIMIZED_SAFE_MATH (1) +#else +#define BASE_HAS_OPTIMIZED_SAFE_MATH (0) +#endif + +namespace base { +namespace internal { + +// These are the non-functioning boilerplate implementations of the optimized +// safe math routines. +#if !BASE_HAS_OPTIMIZED_SAFE_MATH +template <typename T, typename U> +struct CheckedAddFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr bool Do(T, U, V*) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<bool>(); + } +}; + +template <typename T, typename U> +struct CheckedSubFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr bool Do(T, U, V*) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<bool>(); + } +}; + +template <typename T, typename U> +struct CheckedMulFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr bool Do(T, U, V*) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<bool>(); + } +}; + +template <typename T, typename U> +struct ClampedAddFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; + +template <typename T, typename U> +struct ClampedSubFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; + +template <typename T, typename U> +struct ClampedMulFastOp { + static const bool is_supported = false; + template <typename V> + static constexpr V Do(T, U) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<V>(); + } +}; + +template <typename T> +struct ClampedNegFastOp { + static const bool is_supported = false; + static constexpr T Do(T) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure<T>(); + } +}; +#endif // BASE_HAS_OPTIMIZED_SAFE_MATH +#undef BASE_HAS_OPTIMIZED_SAFE_MATH + +// This is used for UnsignedAbs, where we need to support floating-point +// template instantiations even though we don't actually support the operations. +// However, there is no corresponding implementation of e.g. SafeUnsignedAbs, +// so the float versions will not compile. +template <typename Numeric, + bool IsInteger = std::is_integral<Numeric>::value, + bool IsFloat = std::is_floating_point<Numeric>::value> +struct UnsignedOrFloatForSize; + +template <typename Numeric> +struct UnsignedOrFloatForSize<Numeric, true, false> { + using type = typename std::make_unsigned<Numeric>::type; +}; + +template <typename Numeric> +struct UnsignedOrFloatForSize<Numeric, false, true> { + using type = Numeric; +}; + +// Wrap the unary operations to allow SFINAE when instantiating integrals versus +// floating points. These don't perform any overflow checking. Rather, they +// exhibit well-defined overflow semantics and rely on the caller to detect +// if an overflow occured. + +template <typename T, + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> +constexpr T NegateWrapper(T value) { + using UnsignedT = typename std::make_unsigned<T>::type; + // This will compile to a NEG on Intel, and is normal negation on ARM. + return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> +constexpr T NegateWrapper(T value) { + return -value; +} + +template <typename T, + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> +constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { + return ~value; +} + +template <typename T, + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> +constexpr T AbsWrapper(T value) { + return static_cast<T>(SafeUnsignedAbs(value)); +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> +constexpr T AbsWrapper(T value) { + return value < 0 ? -value : value; +} + +template <template <typename, typename, typename> class M, + typename L, + typename R> +struct MathWrapper { + using math = M<typename UnderlyingType<L>::type, + typename UnderlyingType<R>::type, + void>; + using type = typename math::result_type; +}; + +// These variadic templates work out the return types. +// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support. +template <template <typename, typename, typename> class M, + typename L, + typename R, + typename... Args> +struct ResultType; + +template <template <typename, typename, typename> class M, + typename L, + typename R> +struct ResultType<M, L, R> { + using type = typename MathWrapper<M, L, R>::type; +}; + +template <template <typename, typename, typename> class M, + typename L, + typename R, + typename... Args> +struct ResultType { + using type = + typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type; +}; + +// The following macros are just boilerplate for the standard arithmetic +// operator overloads and variadic function templates. A macro isn't the nicest +// solution, but it beats rewriting these over and over again. +#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ + template <typename L, typename R, typename... Args> \ + constexpr CLASS##Numeric< \ + typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \ + CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \ + return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ + args...); \ + } + +#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ + /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ + template <typename L, typename R, \ + typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ + nullptr> \ + constexpr CLASS##Numeric< \ + typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ + operator OP(const L lhs, const R rhs) { \ + return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ + rhs); \ + } \ + /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ + template <typename L> \ + template <typename R> \ + constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ + const R rhs) { \ + return MathOp<CLASS##OP_NAME##Op>(rhs); \ + } \ + /* Variadic arithmetic functions that return CLASS##Numeric. */ \ + BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ |