summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/mozjs
diff options
context:
space:
mode:
authorMatt Cotter <matt.cotter@mongodb.com>2016-08-30 14:43:31 -0400
committerMatt Cotter <matt.cotter@mongodb.com>2016-09-01 16:44:57 -0400
commitebf5539dac43ccc2289fabdd943ff6479a8d3920 (patch)
tree59850026339c54de7a04eb0d11b29bfbfc0a2513 /src/mongo/scripting/mozjs
parentbb6ae8c4cbde96aeb1a8681a4af8ccd8afee43f4 (diff)
downloadmongo-ebf5539dac43ccc2289fabdd943ff6479a8d3920.tar.gz
SERVER-25827 fix UB related to NumberLong(NaN) in shell
Diffstat (limited to 'src/mongo/scripting/mozjs')
-rw-r--r--src/mongo/scripting/mozjs/numberlong.cpp70
1 files changed, 26 insertions, 44 deletions
diff --git a/src/mongo/scripting/mozjs/numberlong.cpp b/src/mongo/scripting/mozjs/numberlong.cpp
index 1f763e47c20..02cd513b1b6 100644
--- a/src/mongo/scripting/mozjs/numberlong.cpp
+++ b/src/mongo/scripting/mozjs/numberlong.cpp
@@ -30,6 +30,7 @@
#include "mongo/scripting/mozjs/numberlong.h"
+#include <boost/optional.hpp>
#include <js/Conversions.h>
#include "mongo/scripting/mozjs/implscope.h"
@@ -38,6 +39,7 @@
#include "mongo/scripting/mozjs/valuewriter.h"
#include "mongo/scripting/mozjs/wrapconstrainedmethod.h"
#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/represent_as.h"
#include "mongo/util/text.h"
namespace mongo {
@@ -127,27 +129,13 @@ void NumberLongInfo::Functions::floatApprox::call(JSContext* cx, JS::CallArgs ar
}
void NumberLongInfo::Functions::top::call(JSContext* cx, JS::CallArgs args) {
- int64_t numLong = NumberLongInfo::ToNumberLong(cx, args.thisv());
-
- // values above 2^53 are not accurately represented in JS
- if (numLong == INT64_MIN || std::abs(numLong) >= 9007199254740992LL) {
- auto val64 = static_cast<unsigned long long>(numLong);
- ValueReader(cx, args.rval()).fromDouble(val64 >> 32);
- } else {
- args.rval().setUndefined();
- }
+ auto numULong = static_cast<uint64_t>(NumberLongInfo::ToNumberLong(cx, args.thisv()));
+ ValueReader(cx, args.rval()).fromDouble(numULong >> 32);
}
void NumberLongInfo::Functions::bottom::call(JSContext* cx, JS::CallArgs args) {
- int64_t numLong = NumberLongInfo::ToNumberLong(cx, args.thisv());
-
- // values above 2^53 are not accurately represented in JS
- if (numLong == INT64_MIN || std::abs(numLong) >= 9007199254740992LL) {
- auto val64 = static_cast<unsigned long long>(numLong);
- ValueReader(cx, args.rval()).fromDouble(val64 & 0x00000000FFFFFFFF);
- } else {
- args.rval().setUndefined();
- }
+ auto numULong = static_cast<uint64_t>(NumberLongInfo::ToNumberLong(cx, args.thisv()));
+ ValueReader(cx, args.rval()).fromDouble(numULong & 0x00000000FFFFFFFF);
}
void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) {
@@ -173,39 +161,33 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) {
if (arg.isInt32()) {
numLong = arg.toInt32();
} else if (arg.isDouble()) {
- numLong = arg.toDouble();
- } else {
- std::string str = ValueWriter(cx, arg).toString();
- int64_t val;
+ auto opt = representAs<int64_t>(arg.toDouble());
+ uassert(ErrorCodes::BadValue,
+ "number passed to NumberLong must be representable as an int64_t",
+ opt);
+ numLong = *opt;
+ } else if (arg.isString()) {
// For string values we call strtoll because we expect non-number string
// values to fail rather than return 0 (which is the behavior of ToInt64).
- if (arg.isString())
- val = parseLL(str.c_str());
- // Otherwise we call the toNumber on the js value to get the int64_t value.
- else
- val = ValueWriter(cx, arg).toInt64();
-
- numLong = val;
+ std::string str = ValueWriter(cx, arg).toString();
+ numLong = parseLL(str.c_str());
+ } else {
+ numLong = ValueWriter(cx, arg).toInt64();
}
} else {
- if (!args.get(0).isNumber())
- uasserted(ErrorCodes::BadValue, "floatApprox must be a number");
-
- double top = 0;
- if (args.get(1).isNumber())
- top = static_cast<double>(static_cast<uint32_t>(args.get(1).toNumber()));
-
- if (!args.get(1).isNumber() || args.get(1).toNumber() != top)
- uasserted(ErrorCodes::BadValue, "top must be a 32 bit unsigned number");
+ uassert(ErrorCodes::BadValue, "floatApprox must be a number", args.get(0).isNumber());
+ uassert(ErrorCodes::BadValue, "top must be a number", args.get(1).isNumber());
+ uassert(ErrorCodes::BadValue, "bottom must be a number", args.get(2).isNumber());
- double bot = 0;
- if (args.get(2).isNumber())
- bot = static_cast<double>(static_cast<uint32_t>(args.get(2).toNumber()));
+ auto topOpt = representAs<uint32_t>(args.get(1).toNumber());
+ uassert(ErrorCodes::BadValue, "top must be a 32 bit unsigned number", topOpt);
+ uint64_t top = *topOpt;
- if (!args.get(2).isNumber() || args.get(2).toNumber() != bot)
- uasserted(ErrorCodes::BadValue, "bottom must be a 32 bit unsigned number");
+ auto botOpt = representAs<uint32_t>(args.get(2).toNumber());
+ uassert(ErrorCodes::BadValue, "bottom must be a 32 bit unsigned number", botOpt);
+ uint64_t bot = *botOpt;
- numLong = (static_cast<uint64_t>(top) << 32) + static_cast<uint64_t>(bot);
+ numLong = (top << 32) + bot;
}
JS_SetPrivate(thisv, new int64_t(numLong));