/* * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CheckedArithmetic_h #define CheckedArithmetic_h #include #include #include #include /* Checked * * This class provides a mechanism to perform overflow-safe integer arithmetic * without having to manually ensure that you have all the required bounds checks * directly in your code. * * There are two modes of operation: * - The default is Checked, and crashes at the point * and overflow has occurred. * - The alternative is Checked, which uses an additional * byte of storage to track whether an overflow has occurred, subsequent * unchecked operations will crash if an overflow has occured * * It is possible to provide a custom overflow handler, in which case you need * to support these functions: * - void overflowed(); * This function is called when an operation has produced an overflow. * - bool hasOverflowed(); * This function must return true if overflowed() has been called on an * instance and false if it has not. * - void clearOverflow(); * Used to reset overflow tracking when a value is being overwritten with * a new value. * * Checked works for all integer types, with the following caveats: * - Mixing signedness of operands is only supported for types narrower than * 64bits. * - It does have a performance impact, so tight loops may want to be careful * when using it. * */ namespace WTF { enum class CheckedState { DidOverflow, DidNotOverflow }; class CrashOnOverflow { public: static NO_RETURN_DUE_TO_CRASH void overflowed() { crash(); } void clearOverflow() { } static NO_RETURN_DUE_TO_CRASH void crash() { CRASH(); } public: bool hasOverflowed() const { return false; } }; class RecordOverflow { protected: RecordOverflow() : m_overflowed(false) { } void overflowed() { m_overflowed = true; } void clearOverflow() { m_overflowed = false; } static NO_RETURN_DUE_TO_CRASH void crash() { CRASH(); } public: bool hasOverflowed() const { return m_overflowed; } private: unsigned char m_overflowed; }; template class Checked; template struct RemoveChecked; template struct RemoveChecked>; template = sizeof(Source), bool targetSigned = std::numeric_limits::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; template struct BoundsChecker { static bool inBounds(Source value) { // Same signedness so implicit type conversion will always increase precision to widest type. return value <= std::numeric_limits::max(); } }; template struct BoundsChecker { static bool inBounds(Source value) { // Same signedness so implicit type conversion will always increase precision to widest type. return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); } }; template struct BoundsChecker { static bool inBounds(Source value) { // When converting value to unsigned Source, value will become a big value if value is negative. // Casted value will become bigger than Target::max as Source is bigger than Target. return static_cast::type>(value) <= std::numeric_limits::max(); } }; template struct BoundsChecker { static bool inBounds(Source value) { // The unsigned Source type has greater precision than the target so max(Target) -> Source will widen. return value <= static_cast(std::numeric_limits::max()); } }; template struct BoundsChecker { static bool inBounds(Source) { // Same sign, greater or same precision. return true; } }; template struct BoundsChecker { static bool inBounds(Source) { // Same sign, greater or same precision. return true; } }; template struct BoundsChecker { static bool inBounds(Source value) { // Target is signed with greater or same precision. If strictly greater, it is always safe. if (sizeof(Target) > sizeof(Source)) return true; return value <= static_cast(std::numeric_limits::max()); } }; template struct BoundsChecker { static bool inBounds(Source value) { // Target is unsigned with greater precision. return value >= 0; } }; template static inline bool isInBounds(Source value) { return BoundsChecker::inBounds(value); } template static inline bool convertSafely(Source input, Target& output) { if (!isInBounds(input)) return false; output = static_cast(input); return true; } template struct RemoveChecked { typedef T CleanType; static const CleanType DefaultValue = 0; }; template struct RemoveChecked> { typedef typename RemoveChecked::CleanType CleanType; static const CleanType DefaultValue = 0; }; template struct RemoveChecked> { typedef typename RemoveChecked::CleanType CleanType; static const CleanType DefaultValue = 0; }; // The ResultBase and SignednessSelector are used to workaround typeof not being // available in MSVC template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; template struct ResultBase { typedef U ResultType; }; template struct ResultBase { typedef V ResultType; }; template struct ResultBase { typedef U ResultType; }; template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; template struct SignednessSelector { typedef U ResultType; }; template struct SignednessSelector { typedef U ResultType; }; template struct SignednessSelector { typedef V ResultType; }; template struct SignednessSelector { typedef U ResultType; }; template struct ResultBase { typedef typename SignednessSelector::ResultType ResultType; }; template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { }; template ::ResultType, bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; template struct ArithmeticOperations { // LHS and RHS are signed types // Helper function static inline bool signsMatch(LHS lhs, RHS rhs) { return (lhs ^ rhs) >= 0; } static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { if (signsMatch(lhs, rhs)) { if (lhs >= 0) { if ((std::numeric_limits::max() - rhs) < lhs) return false; } else { ResultType temp = lhs - std::numeric_limits::min(); if (rhs < -temp) return false; } } // if the signs do not match this operation can't overflow result = lhs + rhs; return true; } static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { if (!signsMatch(lhs, rhs)) { if (lhs >= 0) { if (lhs > std::numeric_limits::max() + rhs) return false; } else { if (rhs > std::numeric_limits::max() + lhs) return false; } } // if the signs match this operation can't overflow result = lhs - rhs; return true; } static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { if (signsMatch(lhs, rhs)) { if (lhs >= 0) { if (lhs && (std::numeric_limits::max() / lhs) < rhs) return false; } else { if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) return false; if ((std::numeric_limits::max() / -lhs) < -rhs) return false; } } else { if (lhs < 0) { if (rhs && lhs < (std::numeric_limits::min() / rhs)) return false; } else { if (lhs && rhs < (std::numeric_limits::min() / lhs)) return false; } } result = lhs * rhs; return true; } static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } }; template struct ArithmeticOperations { // LHS and RHS are unsigned types so bounds checks are nice and easy static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { ResultType temp = lhs + rhs; if (temp < lhs) return false; result = temp; return true; } static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { ResultType temp = lhs - rhs; if (temp > lhs) return false; result = temp; return true; } static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN { if (!lhs || !rhs) { result = 0; return true; } if (std::numeric_limits::max() / lhs < rhs) return false; result = lhs * rhs; return true; } static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } }; template struct ArithmeticOperations { static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) { int64_t temp = lhs + rhs; if (temp < std::numeric_limits::min()) return false; if (temp > std::numeric_limits::max()) return false; result = static_cast(temp); return true; } static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) { int64_t temp = lhs - rhs; if (temp < std::numeric_limits::min()) return false; if (temp > std::numeric_limits::max()) return false; result = static_cast(temp); return true; } static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) { int64_t temp = lhs * rhs; if (temp < std::numeric_limits::min()) return false; if (temp > std::numeric_limits::max()) return false; result = static_cast(temp); return true; } static inline bool equals(int lhs, unsigned rhs) { return static_cast(lhs) == static_cast(rhs); } }; template struct ArithmeticOperations { static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) { return ArithmeticOperations::add(rhs, lhs, result); } static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) { return ArithmeticOperations::sub(lhs, rhs, result); } static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) { return ArithmeticOperations::multiply(rhs, lhs, result); } static inline bool equals(unsigned lhs, int rhs) { return ArithmeticOperations::equals(rhs, lhs); } }; template static inline bool safeAdd(U lhs, V rhs, R& result) { return ArithmeticOperations::add(lhs, rhs, result); } template static inline bool safeSub(U lhs, V rhs, R& result) { return ArithmeticOperations::sub(lhs, rhs, result); } template static inline bool safeMultiply(U lhs, V rhs, R& result) { return ArithmeticOperations::multiply(lhs, rhs, result); } template static inline bool safeEquals(U lhs, V rhs) { return ArithmeticOperations::equals(lhs, rhs); } enum ResultOverflowedTag { ResultOverflowed }; template class Checked : public OverflowHandler { public: template friend class Checked; Checked() : m_value(0) { } Checked(ResultOverflowedTag) : m_value(0) { this->overflowed(); } template Checked(U value) { if (!isInBounds(value)) this->overflowed(); m_value = static_cast(value); } template Checked(const Checked& rhs) : m_value(rhs.m_value) { if (rhs.hasOverflowed()) this->overflowed(); } template Checked(const Checked& rhs) : OverflowHandler(rhs) { if (!isInBounds(rhs.m_value)) this->overflowed(); m_value = static_cast(rhs.m_value); } template Checked(const Checked& rhs) { if (rhs.hasOverflowed()) this->overflowed(); if (!isInBounds(rhs.m_value)) this->overflowed(); m_value = static_cast(rhs.m_value); } const Checked& operator=(Checked rhs) { this->clearOverflow(); if (rhs.hasOverflowed()) this->overflowed(); m_value = static_cast(rhs.m_value); return *this; } template const Checked& operator=(U value) { return *this = Checked(value); } template const Checked& operator=(const Checked& rhs) { return *this = Checked(rhs); } // prefix const Checked& operator++() { if (m_value == std::numeric_limits::max()) this->overflowed(); m_value++; return *this; } const Checked& operator--() { if (m_value == std::numeric_limits::min()) this->overflowed(); m_value--; return *this; } // postfix operators const Checked operator++(int) { if (m_value == std::numeric_limits::max()) this->overflowed(); return Checked(m_value++); } const Checked operator--(int) { if (m_value == std::numeric_limits::min()) this->overflowed(); return Checked(m_value--); } // Boolean operators bool operator!() const { if (this->hasOverflowed()) this->crash(); return !m_value; } explicit operator bool() const { if (this->hasOverflowed()) this->crash(); return m_value; } // Value accessors. unsafeGet() will crash if there's been an overflow. T unsafeGet() const { if (this->hasOverflowed()) this->crash(); return m_value; } inline CheckedState safeGet(T& value) const WARN_UNUSED_RETURN { value = m_value; if (this->hasOverflowed()) return CheckedState::DidOverflow; return CheckedState::DidNotOverflow; } // Mutating assignment template const Checked operator+=(U rhs) { if (!safeAdd(m_value, rhs, m_value)) this->overflowed(); return *this; } template const Checked operator-=(U rhs) { if (!safeSub(m_value, rhs, m_value)) this->overflowed(); return *this; } template const Checked operator*=(U rhs) { if (!safeMultiply(m_value, rhs, m_value)) this->overflowed(); return *this; } const Checked operator*=(double rhs) { double result = rhs * m_value; // Handle +/- infinity and NaN if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) this->overflowed(); m_value = (T)result; return *this; } const Checked operator*=(float rhs) { return *this *= (double)rhs; } template const Checked operator+=(Checked rhs) { if (rhs.hasOverflowed()) this->overflowed(); return *this += rhs.m_value; } template const Checked operator-=(Checked rhs) { if (rhs.hasOverflowed()) this->overflowed(); return *this -= rhs.m_value; } template const Checked operator*=(Checked rhs) { if (rhs.hasOverflowed()) this->overflowed(); return *this *= rhs.m_value; } // Equality comparisons template bool operator==(Checked rhs) { return unsafeGet() == rhs.unsafeGet(); } template bool operator==(U rhs) { if (this->hasOverflowed()) this->crash(); return safeEquals(m_value, rhs); } template const Checked operator==(Checked rhs) { return unsafeGet() == Checked(rhs.unsafeGet()); } template bool operator!=(U rhs) { return !(*this == rhs); } // Other comparisons template bool operator<(Checked rhs) const { return unsafeGet() < rhs.unsafeGet(); } bool operator<(T rhs) const { return unsafeGet() < rhs; } template bool operator<=(Checked rhs) const { return unsafeGet() <= rhs.unsafeGet(); } bool operator<=(T rhs) const { return unsafeGet() <= rhs; } template bool operator>(Checked rhs) const { return unsafeGet() > rhs.unsafeGet(); } bool operator>(T rhs) const { return unsafeGet() > rhs; } template bool operator>=(Checked rhs) const { return unsafeGet() >= rhs.unsafeGet(); } bool operator>=(T rhs) const { return unsafeGet() >= rhs; } private: // Disallow implicit conversion of floating point to integer types Checked(float); Checked(double); void operator=(float); void operator=(double); void operator+=(float); void operator+=(double); void operator-=(float); void operator-=(double); T m_value; }; template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) { U x = 0; V y = 0; bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; typename Result::ResultType result = 0; overflowed |= !safeAdd(x, y, result); if (overflowed) return ResultOverflowed; return result; } template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) { U x = 0; V y = 0; bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; typename Result::ResultType result = 0; overflowed |= !safeSub(x, y, result); if (overflowed) return ResultOverflowed; return result; } template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) { U x = 0; V y = 0; bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; typename Result::ResultType result = 0; overflowed |= !safeMultiply(x, y, result); if (overflowed) return ResultOverflowed; return result; } template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) { return lhs + Checked(rhs); } template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) { return lhs - Checked(rhs); } template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) { return lhs * Checked(rhs); } template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) { return Checked(lhs) + rhs; } template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) { return Checked(lhs) - rhs; } template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) { return Checked(lhs) * rhs; } // Convenience typedefs. typedef Checked CheckedInt8; typedef Checked CheckedUint8; typedef Checked CheckedInt16; typedef Checked CheckedUint16; typedef Checked CheckedInt32; typedef Checked CheckedUint32; typedef Checked CheckedInt64; typedef Checked CheckedUint64; typedef Checked CheckedSize; template Checked checkedSum(U value) { return Checked(value); } template Checked checkedSum(U value, Args... args) { return Checked(value) + checkedSum(args...); } // Sometimes, you just want to check if some math would overflow - the code to do the math is // already in place, and you want to guard it. template bool sumOverflows(Args... args) { return checkedSum(args...).hasOverflowed(); } template bool differenceOverflows(U left, U right) { return (Checked(left) - Checked(right)).hasOverflowed(); } template Checked checkedProduct(U value) { return Checked(value); } template Checked checkedProduct(U value, Args... args) { return Checked(value) * checkedProduct(args...); } // Sometimes, you just want to check if some math would overflow - the code to do the math is // already in place, and you want to guard it. template bool productOverflows(Args... args) { return checkedProduct(args...).hasOverflowed(); } } using WTF::Checked; using WTF::CheckedState; using WTF::RecordOverflow; using WTF::CheckedInt8; using WTF::CheckedUint8; using WTF::CheckedInt16; using WTF::CheckedUint16; using WTF::CheckedInt32; using WTF::CheckedUint32; using WTF::CheckedInt64; using WTF::CheckedUint64; using WTF::CheckedSize; using WTF::checkedSum; using WTF::differenceOverflows; using WTF::productOverflows; using WTF::sumOverflows; #endif