summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSvilen Mihaylov <svilen.mihaylov@mongodb.com>2020-05-05 10:18:39 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-05-21 21:19:39 +0000
commit15e384e1938cb8c64dae29f4a02d9da9defecd7f (patch)
treea4c0eb203d533bd4ded76244330858e414ef9071
parent309b6b7171f028bc266abf9f96a9a96569271693 (diff)
downloadmongo-15e384e1938cb8c64dae29f4a02d9da9defecd7f.tar.gz
SERVER-47994 Fix for numerical overflow in GeoHash
-rw-r--r--jstests/core/geo_near_bounds_overflow.js51
-rw-r--r--jstests/libs/geo_near_random.js22
-rw-r--r--src/mongo/db/geo/hash.cpp13
3 files changed, 75 insertions, 11 deletions
diff --git a/jstests/core/geo_near_bounds_overflow.js b/jstests/core/geo_near_bounds_overflow.js
new file mode 100644
index 00000000000..da7cf4ea614
--- /dev/null
+++ b/jstests/core/geo_near_bounds_overflow.js
@@ -0,0 +1,51 @@
+// Tests behavior with invalid 2d bounds.
+(function() {
+ "use strict";
+
+ load('jstests/libs/geo_near_random.js');
+
+ const test = new GeoNearRandomTest('geo_near_bounds_overflow');
+
+ function testBounds(min, max) {
+ const indexBounds = {min: min, max: max};
+ test.insertPts(50, indexBounds, 1, true);
+
+ // Handle case where either 1. indexLambda will fail but not throw. We are
+ // asserting it
+ // works, so the outer lambda generates an exception. 2. indexLambda itself
+ // will throw.
+ const indexLambda = function(t) {
+ return t.ensureIndex({loc: '2d'}, indexBounds);
+ };
+ const assertLambda = function(t, lambda) {
+ assert.commandWorked(lambda(t));
+ };
+ assert.throws(assertLambda, [test.t, indexLambda]);
+
+ test.reset();
+ }
+
+ // Test max = Inf.
+ testBounds(-Math.pow(2, 34), Math.pow(-2147483648, 34));
+
+ // Test min = -Inf.
+ testBounds(-Math.pow(-2147483648, 34), 1);
+
+ // Test min = -Inf and max = Inf.
+ testBounds(-Math.pow(-2147483648, 34), Math.pow(-2147483648, 34));
+
+ // Test min = Nan.
+ testBounds({min: 0 / 0, max: 1});
+
+ // Test max = Nan.
+ testBounds({min: 1, max: 0 / 0});
+
+ // Test min and max = Nan.
+ testBounds({min: 0 / 0, max: 0 / 0});
+
+ // Test min > max.
+ testBounds({min: 1, max: -1});
+
+ // Test min and max very close together.
+ testBounds({min: 0, max: 5.56268e-309});
+}()); \ No newline at end of file
diff --git a/jstests/libs/geo_near_random.js b/jstests/libs/geo_near_random.js
index 91f0167ea31..47ab037be3e 100644
--- a/jstests/libs/geo_near_random.js
+++ b/jstests/libs/geo_near_random.js
@@ -1,13 +1,15 @@
GeoNearRandomTest = function(name) {
this.name = name;
this.t = db[name];
- this.nPts = 0;
+ this.reset();
+ print("Starting getNear test: " + name);
+};
- // reset state
+GeoNearRandomTest.prototype.reset = function reset() {
+ // Reset state
+ this.nPts = 0;
this.t.drop();
Random.srand(1234);
-
- print("starting test: " + name);
};
GeoNearRandomTest.prototype.mkPt = function mkPt(scale, indexBounds) {
@@ -26,7 +28,7 @@ GeoNearRandomTest.prototype.mkPt = function mkPt(scale, indexBounds) {
};
-GeoNearRandomTest.prototype.insertPts = function(nPts, indexBounds, scale) {
+GeoNearRandomTest.prototype.insertPts = function(nPts, indexBounds, scale, skipIndex) {
assert.eq(this.nPts, 0, "insertPoints already called");
this.nPts = nPts;
@@ -36,10 +38,12 @@ GeoNearRandomTest.prototype.insertPts = function(nPts, indexBounds, scale) {
}
assert.writeOK(bulk.execute());
- if (!indexBounds)
- this.t.ensureIndex({loc: '2d'});
- else
- this.t.ensureIndex({loc: '2d'}, indexBounds);
+ if (!skipIndex) {
+ if (!indexBounds)
+ this.t.ensureIndex({loc: '2d'});
+ else
+ this.t.ensureIndex({loc: '2d'}, indexBounds);
+ }
};
GeoNearRandomTest.prototype.assertIsPrefix = function(short, long) {
diff --git a/src/mongo/db/geo/hash.cpp b/src/mongo/db/geo/hash.cpp
index afb60b7a02e..2de57c23af1 100644
--- a/src/mongo/db/geo/hash.cpp
+++ b/src/mongo/db/geo/hash.cpp
@@ -676,7 +676,8 @@ Status GeoHashConverter::parseParameters(const BSONObj& paramDoc,
<< " bits were specified");
}
- if (params->min >= params->max) {
+ const bool rangeValid = params->min < params->max;
+ if (!rangeValid || std::isinf(params->min) || std::isinf(params->max)) {
return Status(ErrorCodes::InvalidOptions,
str::stream() << "region for hash must be valid and have positive area, "
<< "but ["
@@ -687,14 +688,22 @@ Status GeoHashConverter::parseParameters(const BSONObj& paramDoc,
<< "was specified");
}
- double numBuckets = (1024 * 1024 * 1024 * 4.0);
+ constexpr double numBuckets = 4.0 * 1024 * 1024 * 1024;
params->scaling = numBuckets / (params->max - params->min);
+ const bool scalingValid = params->scaling > 0;
+ if (!scalingValid || std::isinf(params->scaling)) {
+ return Status(ErrorCodes::InvalidOptions,
+ str::stream() << "range [" << params->min << ", " << params->max
+ << "] is too small.");
+ }
return Status::OK();
}
GeoHashConverter::GeoHashConverter(const Parameters& params) : _params(params) {
init();
+ uassert(
+ 4799400, "Invalid GeoHashConverter parameters", _params.max - _params.min >= _error / 2);
}
void GeoHashConverter::init() {