summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorTed Tuckman <ted.tuckman@mongodb.com>2019-06-07 12:20:23 -0400
committerTed Tuckman <ted.tuckman@mongodb.com>2019-06-11 09:02:37 -0400
commitdd1295d603e94cc8157238d41aaa909f45446526 (patch)
tree37e276790b2aabc0038296ae9aca4a594fec9fa4 /src/mongo/db
parentb29c22ad4b89dccca63bd2a279c48f47f76093d1 (diff)
downloadmongo-dd1295d603e94cc8157238d41aaa909f45446526.tar.gz
SERVER-40869 Error when coercing out of bounds double to long
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/pipeline/document_value_test.cpp106
-rw-r--r--src/mongo/db/pipeline/value.cpp25
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: