/* * 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 { class CrashOnOverflow { protected: NO_RETURN_DUE_TO_CRASH void overflowed() { CRASH(); } void clearOverflow() { } public: bool hasOverflowed() const { return false; } }; class RecordOverflow { protected: RecordOverflow() : m_overflowed(false) { } void overflowed() { m_overflowed = true; } void clearOverflow() { m_overflowed = false; } public: bool hasOverflowed() const { return m_overflowed; } private: unsigned char m_overflowed; }; template class Checked; template struct RemoveChecked; template struct RemoveChecked >; template ::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) { // Target is unsigned so any value less than zero is clearly unsafe if (value < 0) return false; // If our (unsigned) Target is the same or greater width we can // convert value to type Target without losing precision if (sizeof(Target) >= sizeof(Source)) return static_cast(value) <= std::numeric_limits::max(); // The signed 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 value) { // Signed target with an unsigned source if (sizeof(Target) <= sizeof(Source)) return value <= static_cast(std::numeric_limits::max()); // Target is Wider than Source so we're guaranteed to fit any value in // unsigned Source return true; } }; template ::value> struct BoundsCheckElider; template struct BoundsCheckElider { static bool inBounds(Source) { return true; } }; template struct BoundsCheckElider : public BoundsChecker { }; template static inline bool isInBounds(Source value) { return BoundsCheckElider::inBounds(value); } 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 (lhs == std::numeric_limits::min() || 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 { ResultType temp = lhs * rhs; if (temp < lhs) return false; result = temp; 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 }; // FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 static inline bool workAroundClangBug() { return true; } template class Checked : public OverflowHandler { public: template friend class Checked; Checked() : m_value(0) { } Checked(ResultOverflowedTag) : m_value(0) { // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 if (workAroundClangBug()) 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()) CRASH(); return !m_value; } typedef void* (Checked::*UnspecifiedBoolType); operator UnspecifiedBoolType*() const { if (this->hasOverflowed()) CRASH(); return (m_value) ? reinterpret_cast(1) : 0; } // Value accessors. unsafeGet() will crash if there's been an overflow. T unsafeGet() const { if (this->hasOverflowed()) CRASH(); return m_value; } bool safeGet(T& value) const WARN_UNUSED_RETURN { value = m_value; return this->hasOverflowed(); } // 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->overflowed(); return safeEquals(m_value, rhs); } template const Checked operator==(Checked rhs) { return unsafeGet() == Checked(rhs.unsafeGet()); } template bool operator!=(U rhs) { return !(*this == 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) || rhs.safeGet(y); 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) || rhs.safeGet(y); 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) || rhs.safeGet(y); 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; } } using WTF::Checked; using WTF::RecordOverflow; #endif