diff options
author | Bernard Gorman <bernard.gorman@mongodb.com> | 2019-10-06 20:05:52 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-10-06 20:05:52 +0000 |
commit | 21d8699ed6c517b45e1613e20231cd8eba894985 (patch) | |
tree | f75add72f6e243b9c96ced881bc9709f7a6cdb7e /src/mongo/platform | |
parent | 150dfeceb607bb40bbae2e5a49ad9bca08f13e25 (diff) | |
download | mongo-21d8699ed6c517b45e1613e20231cd8eba894985.tar.gz |
SERVER-43699 $mod should not overflow for large negative values
Diffstat (limited to 'src/mongo/platform')
-rw-r--r-- | src/mongo/platform/overflow_arithmetic.h | 14 | ||||
-rw-r--r-- | src/mongo/platform/overflow_arithmetic_test.cpp | 12 |
2 files changed, 26 insertions, 0 deletions
diff --git a/src/mongo/platform/overflow_arithmetic.h b/src/mongo/platform/overflow_arithmetic.h index 74567fe2763..cc38f666178 100644 --- a/src/mongo/platform/overflow_arithmetic.h +++ b/src/mongo/platform/overflow_arithmetic.h @@ -36,6 +36,7 @@ #endif #include "mongo/stdx/type_traits.h" +#include "mongo/util/assert_util.h" namespace mongo::overflow { @@ -81,4 +82,17 @@ constexpr bool sub(stdx::type_identity_t<T> a, stdx::type_identity_t<T> b, T* r) #endif } +/** + * Safe mod function which throws if the divisor is 0 and avoids potential overflow in cases where + * the divisor is -1. If the absolute value of the divisor is 1, mod will always return 0. We fast- + * path this to avoid the scenario where the dividend is the smallest negative long or int value and + * the divisor is -1. Naively performing this % may result in an overflow when the -2^N value is + * divided and becomes 2^N. See SERVER-43699. + */ +template <typename T> +constexpr T safeMod(T dividend, T divisor) { + uassert(51259, "can't mod by zero", divisor != 0); + return (divisor == 1 || divisor == -1 ? 0 : dividend % divisor); +} + } // namespace mongo::overflow diff --git a/src/mongo/platform/overflow_arithmetic_test.cpp b/src/mongo/platform/overflow_arithmetic_test.cpp index 0f0bda5f038..6030883ec58 100644 --- a/src/mongo/platform/overflow_arithmetic_test.cpp +++ b/src/mongo/platform/overflow_arithmetic_test.cpp @@ -173,6 +173,18 @@ TEST(OverflowArithmetic, UnsignedSubtractionTests) { ASSERT(testOflow<T>(f, 0, kMax<T>)); } +TEST(OverflowArithmetic, SafeModTests) { + // Mod -1 should not overflow for LLONG_MIN or INT_MIN. + auto minLong = std::numeric_limits<long long>::min(); + auto minInt = std::numeric_limits<int>::min(); + ASSERT_EQ(overflow::safeMod(minLong, -1LL), 0); + ASSERT_EQ(overflow::safeMod(minInt, -1), 0); + + // A divisor of 0 throws a user assertion. + ASSERT_THROWS_CODE(overflow::safeMod(minLong, 0LL), AssertionException, 51259); + ASSERT_THROWS_CODE(overflow::safeMod(minInt, 0), AssertionException, 51259); +} + TEST(OverflowArithmetic, HeterogeneousArguments) { { int r; |