diff options
author | Ted Tuckman <ted.tuckman@mongodb.com> | 2019-06-07 12:20:23 -0400 |
---|---|---|
committer | Ted Tuckman <ted.tuckman@mongodb.com> | 2019-06-11 09:02:37 -0400 |
commit | dd1295d603e94cc8157238d41aaa909f45446526 (patch) | |
tree | 37e276790b2aabc0038296ae9aca4a594fec9fa4 | |
parent | b29c22ad4b89dccca63bd2a279c48f47f76093d1 (diff) | |
download | mongo-dd1295d603e94cc8157238d41aaa909f45446526.tar.gz |
SERVER-40869 Error when coercing out of bounds double to long
-rw-r--r-- | src/mongo/db/pipeline/document_value_test.cpp | 106 | ||||
-rw-r--r-- | src/mongo/db/pipeline/value.cpp | 25 |
2 files changed, 129 insertions, 2 deletions
diff --git a/src/mongo/db/pipeline/document_value_test.cpp b/src/mongo/db/pipeline/document_value_test.cpp index e758dadf2a3..bb7a6031eff 100644 --- a/src/mongo/db/pipeline/document_value_test.cpp +++ b/src/mongo/db/pipeline/document_value_test.cpp @@ -27,6 +27,8 @@ * it in the license file. */ +#include <math.h> + #include "mongo/platform/basic.h" #include "mongo/bson/bson_depth.h" @@ -1168,8 +1170,8 @@ class LongToInt : public ToIntBase { Value value() { return Value(0xff00000007LL); } - int expected() { - return 7; + bool asserts() { + return true; } }; @@ -1211,6 +1213,46 @@ public: } }; +/** Coerce maxInt to int */ +class MaxIntToInt : public ToIntBase { + Value value() { + return Value((double)std::numeric_limits<int>::max()); + } + int expected() { + return std::numeric_limits<int>::max(); + } +}; + +/** Coerce minInt to int */ +class MinIntToInt : public ToIntBase { + Value value() { + return Value((double)std::numeric_limits<int>::min()); + } + int expected() { + return std::numeric_limits<int>::min(); + } +}; + +/** Coerce maxInt + 1 to int */ +class TooLargeToInt : public ToIntBase { + Value value() { + return Value((double)std::numeric_limits<int>::max() + 1); + } + bool asserts() { + return true; + } +}; + +/** Coerce minInt - 1 to int */ +class TooLargeNegativeToInt : public ToIntBase { + Value value() { + return Value((double)std::numeric_limits<int>::min() - 1); + } + bool asserts() { + return true; + } +}; + class ToLongBase { public: virtual ~ToLongBase() {} @@ -1261,6 +1303,57 @@ class DoubleToLong : public ToLongBase { } }; +/** Coerce infinity to long. */ +class InfToLong : public ToLongBase { + Value value() { + return Value(std::numeric_limits<double>::infinity()); + } + bool asserts() { + return true; + } +}; + +/** Coerce negative infinity to long. **/ +class NegInfToLong : public ToLongBase { + Value value() { + return Value(std::numeric_limits<double>::infinity() * -1); + } + bool asserts() { + return true; + } +}; + +/** Coerce large to long. **/ +class InvalidLargeToLong : public ToLongBase { + Value value() { + return Value(pow(2, 63)); + } + bool asserts() { + return true; + } +}; + +/** Coerce lowest double to long. **/ +class LowestDoubleToLong : public ToLongBase { + Value value() { + return Value(static_cast<double>(std::numeric_limits<long long>::lowest())); + } + long long expected() { + return std::numeric_limits<long long>::lowest(); + } +}; + +/** Coerce 'towards infinity' to long **/ +class TowardsInfinityToLong : public ToLongBase { + Value value() { + return Value(static_cast<double>(std::nextafter(std::numeric_limits<long long>::lowest(), + std::numeric_limits<double>::lowest()))); + } + bool asserts() { + return true; + } +}; + /** Coerce null to long. */ class NullToLong : public ToLongBase { Value value() { @@ -1949,12 +2042,21 @@ public: add<Value::Coerce::NullToInt>(); add<Value::Coerce::UndefinedToInt>(); add<Value::Coerce::StringToInt>(); + add<Value::Coerce::MaxIntToInt>(); + add<Value::Coerce::MinIntToInt>(); + add<Value::Coerce::TooLargeToInt>(); + add<Value::Coerce::TooLargeNegativeToInt>(); add<Value::Coerce::IntToLong>(); add<Value::Coerce::LongToLong>(); add<Value::Coerce::DoubleToLong>(); add<Value::Coerce::NullToLong>(); add<Value::Coerce::UndefinedToLong>(); add<Value::Coerce::StringToLong>(); + add<Value::Coerce::InfToLong>(); + add<Value::Coerce::NegInfToLong>(); + add<Value::Coerce::InvalidLargeToLong>(); + add<Value::Coerce::LowestDoubleToLong>(); + add<Value::Coerce::TowardsInfinityToLong>(); add<Value::Coerce::IntToDouble>(); add<Value::Coerce::LongToDouble>(); add<Value::Coerce::DoubleToDouble>(); diff --git a/src/mongo/db/pipeline/value.cpp b/src/mongo/db/pipeline/value.cpp index e8961ead90a..52a1c5fd71d 100644 --- a/src/mongo/db/pipeline/value.cpp +++ b/src/mongo/db/pipeline/value.cpp @@ -49,6 +49,7 @@ #include "mongo/util/str.h" namespace mongo { + using boost::intrusive_ptr; using std::min; using std::numeric_limits; @@ -472,18 +473,40 @@ bool Value::coerceToBool() const { verify(false); } +namespace { + +template <typename T> +void assertValueInRangeInt(const T& val) { + uassert(31108, + str::stream() << "Can't coerce out of range value " << val << " to int", + val >= std::numeric_limits<int32_t>::min() && + val <= std::numeric_limits<int32_t>::max()); +} + +template <typename T> +void assertValueInRangeLong(const T& val) { + uassert(31109, + str::stream() << "Can't coerce out of range value " << val << " to long", + val >= std::numeric_limits<long long>::min() && + val < BSONElement::kLongLongMaxPlusOneAsDouble); +} +} // namespace + int Value::coerceToInt() const { switch (getType()) { case NumberInt: return _storage.intValue; case NumberLong: + assertValueInRangeInt(_storage.longValue); return static_cast<int>(_storage.longValue); case NumberDouble: + assertValueInRangeInt(_storage.doubleValue); return static_cast<int>(_storage.doubleValue); case NumberDecimal: + assertValueInRangeInt(_storage.getDecimal().toDouble()); return (_storage.getDecimal()).toInt(); default: @@ -503,9 +526,11 @@ long long Value::coerceToLong() const { return static_cast<long long>(_storage.intValue); case NumberDouble: + assertValueInRangeLong(_storage.doubleValue); return static_cast<long long>(_storage.doubleValue); case NumberDecimal: + assertValueInRangeLong(_storage.doubleValue); return (_storage.getDecimal()).toLong(); default: |