summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorRishab Joshi <rishab.joshi@mongodb.com>2022-01-30 22:05:05 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-01-30 22:38:32 +0000
commitd726a1820bef623a3220bf69bb77d8c5be069f42 (patch)
treee7dce5388aa9a04b83be16347cd284e91c1c37e0 /src/mongo
parent4f5c7688766e550d32863923ae6c23c09e2cb5e3 (diff)
downloadmongo-d726a1820bef623a3220bf69bb77d8c5be069f42.tar.gz
SERVER-62902 Fix type conversion issue in JSSRand.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/platform/random.h10
-rw-r--r--src/mongo/shell/shell_utils.cpp36
2 files changed, 30 insertions, 16 deletions
diff --git a/src/mongo/platform/random.h b/src/mongo/platform/random.h
index 3521399c896..def8ddf9067 100644
--- a/src/mongo/platform/random.h
+++ b/src/mongo/platform/random.h
@@ -131,6 +131,16 @@ public:
return std::uniform_int_distribution<int64_t>(0, max - 1)(_urbg);
}
+ /**
+ A number uniformly distributed over all possible values that can be safely represented as
+ double without loosing precision.
+ */
+ int64_t nextInt64SafeDoubleRepresentable() {
+ const int64_t maxRepresentableLimit =
+ static_cast<int64_t>(std::ldexp(1, std::numeric_limits<double>::digits)) + 1;
+ return nextInt64(maxRepresentableLimit);
+ }
+
/** Fill array `buf` with `n` random bytes. */
void fill(void* buf, size_t n) {
const auto p = static_cast<uint8_t*>(buf);
diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp
index aa9b5fe05b2..4c3ca9a7c78 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -63,6 +63,7 @@
#include "mongo/util/fail_point.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/quick_exit.h"
+#include "mongo/util/represent_as.h"
#include "mongo/util/text.h"
#include "mongo/util/version.h"
@@ -306,25 +307,28 @@ BSONObj JSGetMemInfo(const BSONObj& args, void* data) {
thread_local auto _prng = PseudoRandom(0);
BSONObj JSSrand(const BSONObj& a, void* data) {
- int64_t seed;
- // grab the least significant bits of either the supplied argument or
- // a random number from SecureRandom.
+ boost::optional<int64_t> prngSeed = boost::none;
+ boost::optional<int64_t> asDouble = boost::none;
+
+ // Grab the least significant bits of either the supplied argument or a random number from
+ // SecureRandom.
if (a.nFields() == 1 && a.firstElement().isNumber()) {
- seed = a.firstElement().safeNumberLong();
+ asDouble = representAs<double>(a.firstElement().safeNumberLong());
+ prngSeed = asDouble ? representAs<int64_t>(*asDouble) : boost::none;
+ uassert(6290200, "Cannot represent seed as 64 bit integral or double value", prngSeed);
} else {
- seed = SecureRandom().nextInt64();
+ // Use secure random number generator to get the seed value that can be safely
+ // represented as double.
+ auto asInt64 = SecureRandom().nextInt64SafeDoubleRepresentable();
+ asDouble = representAs<double>(asInt64);
+ invariant(asDouble);
+ prngSeed = representAs<int64_t>(*asDouble);
}
- // Make sure the seed is representable as both an int64_t and a double, so that the value we
- // return (as a double) can be fed back in to JSSrand() to initialize the prng (as an int64_t)
- // to the same state. To do so, we cast to the double first which may lose precision for large
- // numbers. Then after the potential precision loss we go back to an int64_t which should not
- // change precision at all. Using that (potentially) new int64_t as the seed, we can now
- // confidently return the double version and know it can be used to set the same exact seed
- // later.
- double asDouble = static_cast<double>(seed);
- int64_t asInt64 = static_cast<int64_t>(asDouble);
- _prng = PseudoRandom(asInt64);
- return BSON("" << asDouble);
+
+ // The seed is representable as both an int64_t and a double, so that the value we return (as a
+ // double) can be fed back in to JSSrand() to initialize the prng (as an int64_t).
+ _prng = PseudoRandom(*prngSeed);
+ return BSON("" << *asDouble);
}
BSONObj JSRand(const BSONObj& a, void* data) {