X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/14957cd040308e3eeec43d26bae5d76da13fcd85..1df5f87f1309a8daa30dabdee855f48ae40d14ab:/wtf/CheckedArithmetic.h diff --git a/wtf/CheckedArithmetic.h b/wtf/CheckedArithmetic.h new file mode 100644 index 0000000..3de506d --- /dev/null +++ b/wtf/CheckedArithmetic.h @@ -0,0 +1,693 @@ +/* + * 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 "Assertions.h" +#include "TypeTraits.h" + +#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: + 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; + } + + 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