diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2020-02-20 17:01:35 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-02-21 16:38:50 +0000 |
commit | 7d5a7b2afe99c12ff8260835bb6d80cbdc0ef48d (patch) | |
tree | b05f3a241ee5f1133ea173e3e45cf0102b31d065 /src/mongo | |
parent | 428ac6507118e58b6709e3dbae9fb4657e377637 (diff) | |
download | mongo-7d5a7b2afe99c12ff8260835bb6d80cbdc0ef48d.tar.gz |
SERVER-46071 Handle nan/inf/out-of-range inputs to integral set parameter values
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/bson/bsonelement.h | 58 | ||||
-rw-r--r-- | src/mongo/idl/server_parameter_with_storage.h | 5 |
2 files changed, 61 insertions, 2 deletions
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h index e3a0e37a2bc..5ad7635ed25 100644 --- a/src/mongo/bson/bsonelement.h +++ b/src/mongo/bson/bsonelement.h @@ -771,6 +771,9 @@ public: bool coerce(Decimal128* out) const; bool coerce(std::vector<std::string>* out) const; + template <typename T> + Status tryCoerce(T* out) const; + /** * Constant double representation of 2^63, the smallest value that will overflow a long long. * @@ -947,6 +950,61 @@ inline long long BSONElement::safeNumberLong() const { } /** + * Attempt to coerce the BSONElement to a primitive type. + * For integral targets, we do additional checking that the + * source file is a finite real number and fits within the + * target type. + */ +template <typename T> +Status BSONElement::tryCoerce(T* out) const { + if constexpr (std::is_integral<T>::value && !std::is_same<bool, T>::value) { + if (type() == NumberDouble) { + double d = numberDouble(); + if (!std::isfinite(d)) { + return {ErrorCodes::BadValue, "Unable to coerce NaN/Inf to integral type"}; + } + if ((d > std::numeric_limits<T>::max()) || (d < std::numeric_limits<T>::lowest())) { + return {ErrorCodes::BadValue, "Out of bounds coercing to integral value"}; + } + } else if (type() == NumberDecimal) { + Decimal128 d = numberDecimal(); + if (!d.isFinite()) { + return {ErrorCodes::BadValue, "Unable to coerce NaN/Inf to integral type"}; + } + if (d.isGreater(Decimal128(std::numeric_limits<T>::max())) || + d.isLess(Decimal128(std::numeric_limits<T>::lowest()))) { + return {ErrorCodes::BadValue, "Out of bounds coercing to integral value"}; + } + } else if (type() == mongo::Bool) { + *out = Bool(); + return Status::OK(); + } + + long long val; + if (!coerce(&val)) { + return {ErrorCodes::BadValue, "Unable to coerce value to integral type"}; + } + + if (std::is_same<long long, T>::value) { + *out = val; + return Status::OK(); + } + + if ((val > std::numeric_limits<T>::max()) || (val < std::numeric_limits<T>::lowest())) { + return {ErrorCodes::BadValue, "Out of bounds coercing to integral value"}; + } + + *out = static_cast<T>(val); + return Status::OK(); + } + + if (!coerce(out)) { + return {ErrorCodes::BadValue, "Unable to coerce value to correct type"}; + } + + return Status::OK(); +} +/** * This safeNumberLongForHash() function does the same thing as safeNumberLong, but it preserves * edge-case behavior from older versions. It's provided for use by hash functions that need to * maintain compatibility with older versions. Don't make any changes to safeNumberLong() without diff --git a/src/mongo/idl/server_parameter_with_storage.h b/src/mongo/idl/server_parameter_with_storage.h index 480983f4ad2..1871aa6156a 100644 --- a/src/mongo/idl/server_parameter_with_storage.h +++ b/src/mongo/idl/server_parameter_with_storage.h @@ -248,8 +248,9 @@ public: Status set(const BSONElement& newValueElement) final { element_type newValue; - if (!newValueElement.coerce(&newValue)) { - return Status(ErrorCodes::BadValue, "Can't coerce value"); + if (auto status = newValueElement.tryCoerce(&newValue); !status.isOK()) { + return {status.code(), + str::stream() << "Failed setting " << name() << ": " << status.reason()}; } return setValue(newValue); |