diff options
author | Geert Bosch <geert.bosch@mongodb.com> | 2019-12-22 14:15:44 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-12-22 14:15:44 +0000 |
commit | 230c60a4f74e09bd5b5dd55112e4ae37be4e94f3 (patch) | |
tree | 5ac3d6cb9935205f6b92e77447bf7f1cb79c45bc /src | |
parent | 8add28c5da7d9906aa3625fce074612f634e1619 (diff) | |
download | mongo-230c60a4f74e09bd5b5dd55112e4ae37be4e94f3.tar.gz |
SERVER-45214 FieldParser::extractNumber fails ubsan for NaNs
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/field_parser.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/field_parser_test.cpp | 68 |
2 files changed, 73 insertions, 2 deletions
diff --git a/src/mongo/db/field_parser.cpp b/src/mongo/db/field_parser.cpp index a72c03b81d2..94207bf7d22 100644 --- a/src/mongo/db/field_parser.cpp +++ b/src/mongo/db/field_parser.cpp @@ -294,7 +294,10 @@ FieldParser::FieldState FieldParser::extractNumber(BSONElement elem, } if (elem.isNumber()) { - *out = elem.numberInt(); + auto num = std::clamp(elem.safeNumberLong(), + static_cast<long long>(std::numeric_limits<int>::min()), + static_cast<long long>(std::numeric_limits<int>::max())); + *out = static_cast<int>(num); return FIELD_SET; } @@ -352,7 +355,7 @@ FieldParser::FieldState FieldParser::extractNumber(BSONElement elem, } if (elem.isNumber()) { - *out = elem.numberLong(); + *out = elem.safeNumberLong(); return FIELD_SET; } diff --git a/src/mongo/db/field_parser_test.cpp b/src/mongo/db/field_parser_test.cpp index dfb0b7f0d7a..adf8e2c59dc 100644 --- a/src/mongo/db/field_parser_test.cpp +++ b/src/mongo/db/field_parser_test.cpp @@ -27,12 +27,14 @@ * it in the license file. */ +#include <limits> #include <map> #include <string> #include <vector> #include "mongo/db/field_parser.h" #include "mongo/db/jsobj.h" +#include "mongo/platform/decimal128.h" #include "mongo/unittest/unittest.h" #include "mongo/util/time_support.h" @@ -466,4 +468,70 @@ TEST(EdgeCases, EmbeddedNullStrings) { ASSERT_EQUALS(errMsg, ""); } +TEST(ExtractNumber, IntCases) { + const int initialNum = 123; + const int defaultNum = 42; + const int minNum = INT_MIN; + const int maxNum = INT_MAX; + const auto decimalNum = mongo::Decimal128("-1.50"); + auto numbers = BSON("tooSmall" << LLONG_MIN << "tooLarge" << (1LL << 31) << "infinity" + << std::numeric_limits<double>::infinity() << "minusInfinity" + << -std::numeric_limits<double>::infinity() << "NaN" + << std::numeric_limits<double>::quiet_NaN() << "hugeDouble" + << 9.9E+99 << "decimal" << decimalNum << "int" << defaultNum); + + int num = initialNum; + auto tooSmallField = BSONField<int>("tooSmall"); + auto tooLargeField = BSONField<int>("tooLarge"); + auto infinityField = BSONField<int>("infinity"); + auto minusInfinityField = BSONField<int>("minusInfinity"); + auto NaNField = BSONField<int>("NaN"); + auto hugeField = BSONField<int>("hugeDouble"); + auto decimalField = BSONField<int>("decimal"); + auto intField = BSONField<int>("int"); + auto missingField = BSONField<int>("missing"); + auto defaultedField = BSONField<int>("defaulted", defaultNum); + + // Failure case. + FieldParser::FieldState fs = FieldParser::extractNumber(numbers, missingField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_NONE); + ASSERT_EQ(num, initialNum); + + // Success cases. + fs = FieldParser::extractNumber(numbers, defaultedField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_DEFAULT); + ASSERT_EQ(num, defaultNum); + + fs = FieldParser::extractNumber(numbers, tooSmallField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, minNum); + + fs = FieldParser::extractNumber(numbers, tooLargeField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, maxNum); + + fs = FieldParser::extractNumber(numbers, hugeField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, maxNum); + + fs = FieldParser::extractNumber(numbers, minusInfinityField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, minNum); + + fs = FieldParser::extractNumber(numbers, infinityField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, maxNum); + + fs = FieldParser::extractNumber(numbers, intField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, defaultNum); + + fs = FieldParser::extractNumber(numbers, NaNField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, 0); + + fs = FieldParser::extractNumber(numbers, decimalField, &num); + ASSERT_EQ(fs, FieldParser::FieldState::FIELD_SET); + ASSERT_EQ(num, -2); +} } // unnamed namespace |