diff options
author | Svilen Mihaylov <svilen.mihaylov@mongodb.com> | 2020-05-05 10:18:39 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-05-21 21:19:39 +0000 |
commit | 15e384e1938cb8c64dae29f4a02d9da9defecd7f (patch) | |
tree | a4c0eb203d533bd4ded76244330858e414ef9071 | |
parent | 309b6b7171f028bc266abf9f96a9a96569271693 (diff) | |
download | mongo-15e384e1938cb8c64dae29f4a02d9da9defecd7f.tar.gz |
SERVER-47994 Fix for numerical overflow in GeoHash
-rw-r--r-- | jstests/core/geo_near_bounds_overflow.js | 51 | ||||
-rw-r--r-- | jstests/libs/geo_near_random.js | 22 | ||||
-rw-r--r-- | src/mongo/db/geo/hash.cpp | 13 |
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() { |