summaryrefslogtreecommitdiff
path: root/src/mongo/platform
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@mongodb.com>2019-10-06 20:05:52 +0000
committerevergreen <evergreen@mongodb.com>2019-10-06 20:05:52 +0000
commit21d8699ed6c517b45e1613e20231cd8eba894985 (patch)
treef75add72f6e243b9c96ced881bc9709f7a6cdb7e /src/mongo/platform
parent150dfeceb607bb40bbae2e5a49ad9bca08f13e25 (diff)
downloadmongo-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.h14
-rw-r--r--src/mongo/platform/overflow_arithmetic_test.cpp12
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;