summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRishab Joshi <rishab.joshi@mongodb.com>2022-02-24 12:01:14 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-24 13:47:41 +0000
commitb57316ee3c29a90de5556c8f60b211c1919f7622 (patch)
treed1a6ab5783440bb83834a802859d602f3ccd1e8d
parent8903391a8e27149346c501119e27822a91ad0691 (diff)
downloadmongo-b57316ee3c29a90de5556c8f60b211c1919f7622.tar.gz
SERVER-62902 Fix type conversion issue in JSSRand.
-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 79f89b28c57..e4ff0454131 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -62,6 +62,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"
@@ -305,25 +306,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) {