commit 9dece22663505c69487701fff625436528063cb2
parent b938fe44ff7c1977d2b750e23c97c6df5c7e5099
Author: serge-sans-paille <sguelton@mozilla.com>
Date: Thu, 2 Oct 2025 05:11:43 +0000
Bug 1991455 - Harmonize __builin_(add|sub|mul)_overflow usage r=emilio,nbp,profiler-reviewers,canaltinova
We used to have __builtin_(...) wrapped as MOZ_ADD_OVERFLOW, SafeAdd or
used directly. Get rid of MOZ_ADD_OVERFLOW, promotes SafeAdd et cie to
mfbt and use the mfbt version everywhere.
Differential Revision: https://phabricator.services.mozilla.com/D266685
Diffstat:
22 files changed, 120 insertions(+), 187 deletions(-)
diff --git a/js/public/Utility.h b/js/public/Utility.h
@@ -10,6 +10,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/Compiler.h"
#include "mozilla/Likely.h"
#include "mozilla/UniquePtr.h"
@@ -556,7 +557,7 @@ namespace js {
template <typename T>
[[nodiscard]] inline bool CalculateAllocSize(size_t numElems,
size_t* bytesOut) {
- return !__builtin_mul_overflow(numElems, sizeof(T), bytesOut);
+ return mozilla::SafeMul(numElems, sizeof(T), bytesOut);
}
/*
@@ -568,8 +569,8 @@ template <typename T, typename Extra>
[[nodiscard]] inline bool CalculateAllocSizeWithExtra(size_t numExtra,
size_t* bytesOut) {
size_t tmp;
- return !__builtin_mul_overflow(numExtra, sizeof(Extra), &tmp) &&
- !__builtin_add_overflow(sizeof(T), tmp, bytesOut);
+ return mozilla::SafeMul(numExtra, sizeof(Extra), &tmp) &&
+ mozilla::SafeAdd(sizeof(T), tmp, bytesOut);
}
} /* namespace js */
@@ -626,7 +627,7 @@ static MOZ_ALWAYS_INLINE T* js_pod_arena_realloc(arena_id_t arena, T* prior,
size_t oldSize,
size_t newSize) {
[[maybe_unused]] size_t tmp;
- MOZ_ASSERT(!__builtin_mul_overflow(oldSize, sizeof(T), &tmp));
+ MOZ_ASSERT(mozilla::SafeMul(oldSize, sizeof(T), &tmp));
size_t bytes;
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
return nullptr;
diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
@@ -8,6 +8,7 @@
#define ds_LifoAlloc_h
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PodOperations.h"
@@ -1212,9 +1213,9 @@ class LifoAllocPolicy {
return nullptr;
}
size_t oldLength;
- [[maybe_unused]] bool overflows =
- __builtin_mul_overflow(oldSize, sizeof(T), &oldLength);
- MOZ_ASSERT(!overflows);
+ [[maybe_unused]] bool nooverflow =
+ mozilla::SafeMul(oldSize, sizeof(T), &oldLength);
+ MOZ_ASSERT(nooverflow);
memcpy(n, p, std::min(oldLength, newSize * sizeof(T)));
return n;
}
diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp
@@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/Likely.h"
#include "mozilla/ScopeExit.h"
@@ -256,7 +257,7 @@ class MOZ_STACK_CLASS BaselineStackBuilder {
MOZ_ASSERT(header_ != nullptr);
size_t newSize;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(bufferTotal_, 2, &newSize))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(bufferTotal_, size_t(2), &newSize))) {
ReportOutOfMemory(cx_);
return false;
}
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
@@ -7,6 +7,7 @@
#include "jit/CodeGenerator.h"
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/EnumeratedArray.h"
@@ -60,7 +61,6 @@
#include "js/ScalarType.h" // js::Scalar::Type
#include "proxy/DOMProxy.h"
#include "proxy/ScriptedProxyHandler.h"
-#include "util/CheckedArithmetic.h"
#include "util/DifferentialTesting.h"
#include "util/Unicode.h"
#include "vm/ArrayBufferViewObject.h"
@@ -15457,7 +15457,8 @@ void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
if (lir->index()->isConstant()) {
int32_t nmin, nmax;
int32_t index = ToInt32(lir->index());
- if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
+ if (mozilla::SafeAdd(index, min, &nmin) &&
+ mozilla::SafeAdd(index, max, &nmax) && nmin >= 0) {
if (length->isGeneralReg()) {
bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax);
} else {
@@ -15488,7 +15489,7 @@ void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
if (min != 0) {
int32_t diff;
- if (SafeSub(max, min, &diff)) {
+ if (mozilla::SafeSub(max, min, &diff)) {
max = diff;
} else {
if (type == MIRType::Int32) {
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
@@ -6,6 +6,7 @@
#include "jit/IonAnalysis.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/HashFunctions.h"
#include <algorithm>
@@ -16,7 +17,6 @@
#include "jit/DominatorTree.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
-#include "util/CheckedArithmetic.h"
#include "vm/BytecodeUtil-inl.h"
@@ -3751,7 +3751,7 @@ SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space,
int32_t constant;
if (space == MathSpace::Modulo) {
constant = uint32_t(lsum.constant) + uint32_t(rsum.constant);
- } else if (!SafeAdd(lsum.constant, rsum.constant, &constant) ||
+ } else if (!mozilla::SafeAdd(lsum.constant, rsum.constant, &constant) ||
!MonotoneAdd(lsum.constant, rsum.constant)) {
return SimpleLinearSum(ins, 0);
}
@@ -3764,7 +3764,7 @@ SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space,
int32_t constant;
if (space == MathSpace::Modulo) {
constant = uint32_t(lsum.constant) - uint32_t(rsum.constant);
- } else if (!SafeSub(lsum.constant, rsum.constant, &constant) ||
+ } else if (!mozilla::SafeSub(lsum.constant, rsum.constant, &constant) ||
!MonotoneSub(lsum.constant, rsum.constant)) {
return SimpleLinearSum(ins, 0);
}
@@ -3805,7 +3805,7 @@ bool jit::ExtractLinearInequality(const MTest* test, BranchDirection direction,
SimpleLinearSum lsum = ExtractLinearSum(lhs);
SimpleLinearSum rsum = ExtractLinearSum(rhs);
- if (!SafeSub(lsum.constant, rsum.constant, &lsum.constant)) {
+ if (!mozilla::SafeSub(lsum.constant, rsum.constant, &lsum.constant)) {
return false;
}
@@ -3816,7 +3816,7 @@ bool jit::ExtractLinearInequality(const MTest* test, BranchDirection direction,
break;
case JSOp::Lt:
/* x < y ==> x + 1 <= y */
- if (!SafeAdd(lsum.constant, 1, &lsum.constant)) {
+ if (!mozilla::SafeAdd(lsum.constant, 1, &lsum.constant)) {
return false;
}
*plessEqual = true;
@@ -3826,7 +3826,7 @@ bool jit::ExtractLinearInequality(const MTest* test, BranchDirection direction,
break;
case JSOp::Gt:
/* x > y ==> x - 1 >= y */
- if (!SafeSub(lsum.constant, 1, &lsum.constant)) {
+ if (!mozilla::SafeSub(lsum.constant, 1, &lsum.constant)) {
return false;
}
*plessEqual = false;
@@ -3890,18 +3890,20 @@ static bool TryEliminateBoundsCheck(BoundsCheckMap& checks, size_t blockIndex,
// Normalize the ranges according to the constant offsets in the two indexes.
int32_t minimumA, maximumA, minimumB, maximumB;
- if (!SafeAdd(sumA.constant, dominating->minimum(), &minimumA) ||
- !SafeAdd(sumA.constant, dominating->maximum(), &maximumA) ||
- !SafeAdd(sumB.constant, dominated->minimum(), &minimumB) ||
- !SafeAdd(sumB.constant, dominated->maximum(), &maximumB)) {
+ if (!mozilla::SafeAdd(sumA.constant, dominating->minimum(), &minimumA) ||
+ !mozilla::SafeAdd(sumA.constant, dominating->maximum(), &maximumA) ||
+ !mozilla::SafeAdd(sumB.constant, dominated->minimum(), &minimumB) ||
+ !mozilla::SafeAdd(sumB.constant, dominated->maximum(), &maximumB)) {
return false;
}
// Update the dominating check to cover both ranges, denormalizing the
// result per the constant offset in the index.
int32_t newMinimum, newMaximum;
- if (!SafeSub(std::min(minimumA, minimumB), sumA.constant, &newMinimum) ||
- !SafeSub(std::max(maximumA, maximumB), sumA.constant, &newMaximum)) {
+ if (!mozilla::SafeSub(std::min(minimumA, minimumB), sumA.constant,
+ &newMinimum) ||
+ !mozilla::SafeSub(std::max(maximumA, maximumB), sumA.constant,
+ &newMaximum)) {
return false;
}
@@ -4555,11 +4557,11 @@ bool jit::AddKeepAliveInstructions(MIRGraph& graph) {
bool LinearSum::multiply(int32_t scale) {
for (size_t i = 0; i < terms_.length(); i++) {
- if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale)) {
+ if (!mozilla::SafeMul(scale, terms_[i].scale, &terms_[i].scale)) {
return false;
}
}
- return SafeMul(scale, constant_, &constant_);
+ return mozilla::SafeMul(scale, constant_, &constant_);
}
bool LinearSum::divide(uint32_t scale) {
@@ -4585,7 +4587,7 @@ bool LinearSum::divide(uint32_t scale) {
bool LinearSum::add(const LinearSum& other, int32_t scale /* = 1 */) {
for (size_t i = 0; i < other.terms_.length(); i++) {
int32_t newScale = scale;
- if (!SafeMul(scale, other.terms_[i].scale, &newScale)) {
+ if (!mozilla::SafeMul(scale, other.terms_[i].scale, &newScale)) {
return false;
}
if (!add(other.terms_[i].term, newScale)) {
@@ -4593,7 +4595,7 @@ bool LinearSum::add(const LinearSum& other, int32_t scale /* = 1 */) {
}
}
int32_t newConstant = scale;
- if (!SafeMul(scale, other.constant_, &newConstant)) {
+ if (!mozilla::SafeMul(scale, other.constant_, &newConstant)) {
return false;
}
return add(newConstant);
@@ -4605,7 +4607,7 @@ bool LinearSum::add(SimpleLinearSum other, int32_t scale) {
}
int32_t constant;
- if (!SafeMul(other.constant, scale, &constant)) {
+ if (!mozilla::SafeMul(other.constant, scale, &constant)) {
return false;
}
@@ -4621,7 +4623,7 @@ bool LinearSum::add(MDefinition* term, int32_t scale) {
if (MConstant* termConst = term->maybeConstantValue()) {
int32_t constant = termConst->toInt32();
- if (!SafeMul(constant, scale, &constant)) {
+ if (!mozilla::SafeMul(constant, scale, &constant)) {
return false;
}
return add(constant);
@@ -4629,7 +4631,7 @@ bool LinearSum::add(MDefinition* term, int32_t scale) {
for (size_t i = 0; i < terms_.length(); i++) {
if (term == terms_[i].term) {
- if (!SafeAdd(scale, terms_[i].scale, &terms_[i].scale)) {
+ if (!mozilla::SafeAdd(scale, terms_[i].scale, &terms_[i].scale)) {
return false;
}
if (terms_[i].scale == 0) {
@@ -4649,7 +4651,7 @@ bool LinearSum::add(MDefinition* term, int32_t scale) {
}
bool LinearSum::add(int32_t constant) {
- return SafeAdd(constant, constant_, &constant_);
+ return mozilla::SafeAdd(constant, constant_, &constant_);
}
void LinearSum::dump(GenericPrinter& out) const {
diff --git a/js/src/jit/JitAllocPolicy.h b/js/src/jit/JitAllocPolicy.h
@@ -9,6 +9,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/Likely.h"
#include "mozilla/OperatorNewExtensions.h"
@@ -103,9 +104,9 @@ class JitAllocPolicy {
return n;
}
size_t oldLength;
- [[maybe_unused]] bool overflows =
- __builtin_mul_overflow(oldSize, sizeof(T), &oldLength);
- MOZ_ASSERT(!overflows);
+ [[maybe_unused]] bool nooverflow =
+ mozilla::SafeMul(oldSize, sizeof(T), &oldLength);
+ MOZ_ASSERT(nooverflow);
memcpy(n, p, std::min(oldLength, newSize * sizeof(T)));
return n;
}
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
@@ -6,6 +6,7 @@
#include "jit/RangeAnalysis.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/MathAlgorithms.h"
#include <algorithm>
@@ -21,7 +22,6 @@
#include "jit/MIRGraph.h"
#include "js/Conversions.h"
#include "js/ScalarType.h" // js::Scalar::Type
-#include "util/CheckedArithmetic.h"
#include "util/Unicode.h"
#include "vm/ArgumentsObject.h"
#include "vm/Float16.h"
@@ -284,7 +284,7 @@ bool RangeAnalysis::addBetaNodes() {
if (val->type() == MIRType::Int32) {
int32_t intbound;
if (NumberEqualsInt32(bound, &intbound) &&
- SafeSub(intbound, 1, &intbound)) {
+ mozilla::SafeSub(intbound, 1, &intbound)) {
bound = intbound;
}
}
@@ -303,7 +303,7 @@ bool RangeAnalysis::addBetaNodes() {
if (val->type() == MIRType::Int32) {
int32_t intbound;
if (NumberEqualsInt32(bound, &intbound) &&
- SafeAdd(intbound, 1, &intbound)) {
+ mozilla::SafeAdd(intbound, 1, &intbound)) {
bound = intbound;
}
}
@@ -2079,7 +2079,7 @@ LoopIterationBound* RangeAnalysis::analyzeLoopIterationCount(
MDefinition* temp = lhs.term;
lhs.term = rhs;
rhs = temp;
- if (!SafeSub(0, lhs.constant, &lhs.constant)) {
+ if (!mozilla::SafeSub(0, lhs.constant, &lhs.constant)) {
return nullptr;
}
lessEqual = !lessEqual;
@@ -2160,7 +2160,7 @@ LoopIterationBound* RangeAnalysis::analyzeLoopIterationCount(
}
int32_t lhsConstant;
- if (!SafeSub(0, lhs.constant, &lhsConstant)) {
+ if (!mozilla::SafeSub(0, lhs.constant, &lhsConstant)) {
return nullptr;
}
if (!iterationBound.add(lhsConstant)) {
@@ -2245,7 +2245,7 @@ void RangeAnalysis::analyzeLoopPhi(const LoopIterationBound* loopBound,
}
int32_t negativeConstant;
- if (!SafeSub(0, modified.constant, &negativeConstant) ||
+ if (!mozilla::SafeSub(0, modified.constant, &negativeConstant) ||
!limitSum.add(negativeConstant)) {
return;
}
@@ -2343,10 +2343,10 @@ bool RangeAnalysis::tryHoistBoundsCheck(const MBasicBlock* header,
// lowerTerm >= -lowerConstant - indexConstant
int32_t lowerConstant = 0;
- if (!SafeSub(lowerConstant, index.constant, &lowerConstant)) {
+ if (!mozilla::SafeSub(lowerConstant, index.constant, &lowerConstant)) {
return false;
}
- if (!SafeSub(lowerConstant, lower->sum.constant(), &lowerConstant)) {
+ if (!mozilla::SafeSub(lowerConstant, lower->sum.constant(), &lowerConstant)) {
return false;
}
@@ -2356,7 +2356,7 @@ bool RangeAnalysis::tryHoistBoundsCheck(const MBasicBlock* header,
// upperTerm + upperConstant < boundsLength
int32_t upperConstant = index.constant;
- if (!SafeAdd(upper->sum.constant(), upperConstant, &upperConstant)) {
+ if (!mozilla::SafeAdd(upper->sum.constant(), upperConstant, &upperConstant)) {
return false;
}
diff --git a/js/src/util/CheckedArithmetic.h b/js/src/util/CheckedArithmetic.h
@@ -1,92 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: set ts=8 sts=2 et sw=2 tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef util_CheckedArithmetic_h
-#define util_CheckedArithmetic_h
-
-#include "mozilla/Compiler.h"
-#include "mozilla/MathAlgorithms.h"
-
-#include <stdint.h>
-
-// This macro is should be `one' if current compiler supports builtin functions
-// like __builtin_sadd_overflow.
-#if MOZ_IS_GCC
-// GCC supports these functions.
-# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1
-#else
-// For CLANG, we use its own function to check for this.
-# ifdef __has_builtin
-# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x)
-# endif
-#endif
-#ifndef BUILTIN_CHECKED_ARITHMETIC_SUPPORTED
-# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 0
-#endif
-
-namespace js {
-
-[[nodiscard]] inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) {
-#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow)
- // Using compiler's builtin function.
- return !__builtin_sadd_overflow(one, two, res);
-#else
- // Use unsigned for the 32-bit operation since signed overflow gets
- // undefined behavior.
- *res = uint32_t(one) + uint32_t(two);
- int64_t ores = (int64_t)one + (int64_t)two;
- return ores == (int64_t)*res;
-#endif
-}
-
-[[nodiscard]] inline bool SafeSub(int32_t one, int32_t two, int32_t* res) {
-#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_ssub_overflow)
- return !__builtin_ssub_overflow(one, two, res);
-#else
- *res = uint32_t(one) - uint32_t(two);
- int64_t ores = (int64_t)one - (int64_t)two;
- return ores == (int64_t)*res;
-#endif
-}
-
-[[nodiscard]] inline bool SafeMul(int32_t one, int32_t two, int32_t* res) {
-#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_smul_overflow)
- return !__builtin_smul_overflow(one, two, res);
-#else
- *res = uint32_t(one) * uint32_t(two);
- int64_t ores = (int64_t)one * (int64_t)two;
- return ores == (int64_t)*res;
-#endif
-}
-
-[[nodiscard]] inline bool SafeMul(uint64_t one, uint64_t two, uint64_t* res) {
-#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_mul_overflow)
- return !__builtin_mul_overflow(one, two, res);
-#else
- // Hacker's Delight, 2nd edition, 2-13 Overflow detection, Fig. 2-2.
- int zeroes =
- mozilla::CountLeadingZeroes64(one) + mozilla::CountLeadingZeroes64(two);
- if (zeroes <= 62) {
- return false;
- }
- uint64_t half = one * (two >> 1);
- if (int64_t(half) < 0) {
- return false;
- }
- *res = half * 2;
- if (two & 1) {
- *res += one;
- if (*res < one) {
- return false;
- }
- }
- return true;
-#endif
-}
-
-} /* namespace js */
-
-#endif /* util_CheckedArithmetic_h */
diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp
@@ -79,6 +79,7 @@
#include "vm/BigIntType.h"
#include "mozilla/Casting.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/HashFunctions.h"
@@ -106,7 +107,6 @@
#include "js/Printer.h" // js::GenericPrinter
#include "js/StableStringChars.h"
#include "js/Utility.h"
-#include "util/CheckedArithmetic.h"
#include "util/DifferentialTesting.h"
#include "vm/JSONPrinter.h" // js::JSONPrinter
#include "vm/StaticStrings.h"
@@ -1929,7 +1929,7 @@ BigInt* BigInt::mul(JSContext* cx, HandleBigInt x, HandleBigInt y) {
uint64_t rhs = y->uint64FromAbsNonZero();
uint64_t res;
- if (js::SafeMul(lhs, rhs, &res)) {
+ if (mozilla::SafeMul(lhs, rhs, &res)) {
MOZ_ASSERT(res != 0);
return createFromNonZeroRawUint64(cx, res, resultNegative);
}
@@ -2238,13 +2238,13 @@ BigInt* BigInt::pow(JSContext* cx, HandleBigInt x, HandleBigInt y) {
while (true) {
uint64_t runningSquareStart = runningSquareInt;
uint64_t r;
- if (!js::SafeMul(runningSquareInt, runningSquareInt, &r)) {
+ if (!mozilla::SafeMul(runningSquareInt, runningSquareInt, &r)) {
break;
}
runningSquareInt = r;
if (n & 1) {
- if (!js::SafeMul(resultInt, runningSquareInt, &r)) {
+ if (!mozilla::SafeMul(resultInt, runningSquareInt, &r)) {
// Recover |runningSquare| before we restart the loop.
runningSquareInt = runningSquareStart;
break;
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
@@ -9,12 +9,13 @@
#include "vm/Interpreter.h"
+#include "mozilla/CheckedArithmetic.h"
+
#include "jslibmath.h"
#include "jsmath.h"
#include "jsnum.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
-#include "util/CheckedArithmetic.h"
#include "vm/BigIntType.h"
#include "vm/BytecodeUtil.h" // JSDVG_SEARCH_STACK
#include "vm/JSAtomUtils.h" // AtomizeString
@@ -613,7 +614,7 @@ static MOZ_ALWAYS_INLINE bool AddOperation(JSContext* cx,
if (lhs.isInt32() && rhs.isInt32()) {
int32_t l = lhs.toInt32(), r = rhs.toInt32();
int32_t t;
- if (MOZ_LIKELY(SafeAdd(l, r, &t))) {
+ if (MOZ_LIKELY(mozilla::SafeAdd(l, r, &t))) {
res.setInt32(t);
return true;
}
diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h
@@ -41,6 +41,7 @@
#if defined(__cplusplus)
# include "mozilla/fallible.h"
# include "mozilla/mozalloc_abort.h"
+# include "mozilla/CheckedArithmetic.h"
# include "mozilla/Likely.h"
#endif
#include "mozilla/Attributes.h"
@@ -156,7 +157,7 @@ class InfallibleAllocPolicy {
template <typename T>
T* pod_malloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
reportAllocOverflow();
}
return static_cast<T*>(moz_xmalloc(size));
@@ -170,7 +171,7 @@ class InfallibleAllocPolicy {
template <typename T>
T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNewSize, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) {
reportAllocOverflow();
}
return static_cast<T*>(moz_xrealloc(aPtr, size));
diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp
@@ -32,6 +32,7 @@
#include "nscore.h"
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/FastBernoulliTrial.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/HashTable.h"
@@ -105,7 +106,7 @@ class InfallibleAllocPolicy {
template <typename T>
static T* maybe_pod_malloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
return nullptr;
}
return (T*)gMallocTable.malloc(size);
@@ -119,7 +120,7 @@ class InfallibleAllocPolicy {
template <typename T>
static T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNewSize, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) {
return nullptr;
}
return (T*)gMallocTable.realloc(aPtr, size);
diff --git a/mfbt/AllocPolicy.h b/mfbt/AllocPolicy.h
@@ -13,6 +13,7 @@
#define mozilla_AllocPolicy_h
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/Likely.h"
#include <cstddef>
@@ -77,7 +78,7 @@ class MallocAllocPolicy {
template <typename T>
T* maybe_pod_malloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size)))
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size)))
return nullptr;
return static_cast<T*>(malloc(size));
}
@@ -90,7 +91,7 @@ class MallocAllocPolicy {
template <typename T>
T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNewSize, sizeof(T), &size)))
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size)))
return nullptr;
return static_cast<T*>(realloc(aPtr, size));
}
diff --git a/mfbt/CheckedArithmetic.h b/mfbt/CheckedArithmetic.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Provides checked integers operations, detecting overflow. */
+
+#ifndef mozilla_CheckedArithmetic_h
+#define mozilla_CheckedArithmetic_h
+
+namespace mozilla {
+
+template <typename T>
+[[nodiscard]] constexpr bool SafeAdd(T lhs, T rhs, T* res) {
+ return !__builtin_add_overflow(lhs, rhs, res);
+}
+
+template <typename T>
+[[nodiscard]] constexpr bool SafeSub(T lhs, T rhs, T* res) {
+ return !__builtin_sub_overflow(lhs, rhs, res);
+}
+
+template <typename T>
+[[nodiscard]] constexpr bool SafeMul(T lhs, T rhs, T* res) {
+ return !__builtin_mul_overflow(lhs, rhs, res);
+}
+
+} // namespace mozilla
+
+#endif /* mozilla_CheckedArithmetic_h */
diff --git a/mfbt/CheckedInt.h b/mfbt/CheckedInt.h
@@ -11,6 +11,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/IntegerTypeTraits.h"
#include <cstdint>
@@ -548,16 +549,16 @@ class CheckedInt {
constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
const CheckedInt<T>& aRhs) { \
auto result = T{}; \
- if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \
+ if (MOZ_UNLIKELY(!FUN(aLhs.mValue, aRhs.mValue, &result))) { \
static_assert(detail::IsInRange<T>(0), \
"Integer type can't represent 0"); \
return CheckedInt<T>(T(0), false); \
} \
return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \
}
-MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, __builtin_add_overflow)
-MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, __builtin_sub_overflow)
-MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, __builtin_mul_overflow)
+MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, SafeAdd)
+MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, SafeSub)
+MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, SafeMul)
#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
diff --git a/mfbt/Saturate.h b/mfbt/Saturate.h
@@ -15,6 +15,7 @@
#include <utility>
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
namespace mozilla {
namespace detail {
@@ -53,46 +54,21 @@ class SaturateOp {
T operator-(const T& aRhs) const { return T(mValue) -= aRhs; }
// Compound operators
-
-#if defined(__has_builtin)
-# if __has_builtin(__builtin_add_overflow)
-# define MOZ_ADD_OVERFLOW __builtin_add_overflow
-# endif
-# if __has_builtin(__builtin_sub_overflow)
-# define MOZ_SUB_OVERFLOW __builtin_sub_overflow
-# endif
-#endif
-
const T& operator+=(const T& aRhs) const {
constexpr T min = std::numeric_limits<T>::min();
constexpr T max = std::numeric_limits<T>::max();
-#ifdef MOZ_ADD_OVERFLOW
- if (MOZ_ADD_OVERFLOW(mValue, aRhs, &mValue))
+ if (!mozilla::SafeAdd(mValue, aRhs, &mValue)) {
return mValue = (aRhs > 0 ? max : min);
-#else
-
- if (aRhs > static_cast<T>(0)) {
- mValue = (max - aRhs) < mValue ? max : mValue + aRhs;
- } else {
- mValue = (min - aRhs) > mValue ? min : mValue + aRhs;
}
-#endif
return mValue;
}
const T& operator-=(const T& aRhs) const {
constexpr T min = std::numeric_limits<T>::min();
constexpr T max = std::numeric_limits<T>::max();
-#ifdef MOZ_SUB_OVERFLOW
- if (MOZ_SUB_OVERFLOW(mValue, aRhs, &mValue))
+ if (!mozilla::SafeSub(mValue, aRhs, &mValue)) {
return mValue = (aRhs > 0 ? min : max);
-#else
- if (aRhs > static_cast<T>(0)) {
- mValue = (min + aRhs) > mValue ? min : mValue - aRhs;
- } else {
- mValue = (max + aRhs) < mValue ? max : mValue - aRhs;
}
-#endif
return mValue;
}
diff --git a/mfbt/Vector.h b/mfbt/Vector.h
@@ -17,6 +17,7 @@
#include "mozilla/ArrayUtils.h" // for PointerRangeSize
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
@@ -88,7 +89,7 @@ inline size_t GrowEltsByDoubling(size_t aOldElts, size_t aIncr) {
* for a Vector doesn't overflow ptrdiff_t (see bug 510319).
*/
[[maybe_unused]] size_t tmp;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aOldElts, 4 * EltSize, &tmp))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aOldElts, 4 * EltSize, &tmp))) {
return 0;
}
@@ -111,7 +112,7 @@ inline size_t GrowEltsByDoubling(size_t aOldElts, size_t aIncr) {
* next power of two overflow PTRDIFF_MAX? */
[[maybe_unused]] size_t tmp;
if (MOZ_UNLIKELY(newMinCap < aOldElts ||
- __builtin_mul_overflow(newMinCap, 4 * EltSize, &tmp))) {
+ !mozilla::SafeMul(newMinCap, 4 * EltSize, &tmp))) {
return 0;
}
diff --git a/mfbt/moz.build b/mfbt/moz.build
@@ -33,6 +33,7 @@ EXPORTS.mozilla = [
"Casting.h",
"ChaosMode.h",
"Char16.h",
+ "CheckedArithmetic.h",
"CheckedInt.h",
"CompactPair.h",
"Compiler.h",
diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp
@@ -7,6 +7,7 @@
// This is included first to ensure it doesn't implicitly depend on anything
// else.
#include "mozilla/BufferList.h"
+#include "mozilla/CheckedArithmetic.h"
// It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
// but MFBT cannot use mozalloc.
@@ -15,7 +16,7 @@ class InfallibleAllocPolicy {
template <typename T>
T* pod_malloc(size_t aNumElems) {
size_t size;
- if (__builtin_mul_overflow(aNumElems, sizeof(T), &size)) {
+ if (!mozilla::SafeMul(aNumElems, sizeof(T), &size)) {
MOZ_CRASH("TestBufferList.cpp: overflow");
}
T* rv = static_cast<T*>(malloc(size));
diff --git a/mfbt/tests/TestSegmentedVector.cpp b/mfbt/tests/TestSegmentedVector.cpp
@@ -10,6 +10,7 @@
#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
+#include "mozilla/CheckedArithmetic.h"
using mozilla::SegmentedVector;
@@ -20,7 +21,7 @@ class InfallibleAllocPolicy {
template <typename T>
T* pod_malloc(size_t aNumElems) {
size_t size;
- if (__builtin_mul_overflow(aNumElems, sizeof(T), &size)) {
+ if (!mozilla::SafeMul(aNumElems, sizeof(T), &size)) {
MOZ_CRASH("TestSegmentedVector.cpp: overflow");
}
T* rv = static_cast<T*>(malloc(size));
diff --git a/mozglue/misc/NativeNt.h b/mozglue/misc/NativeNt.h
@@ -17,6 +17,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"
#include "mozilla/Range.h"
@@ -1625,7 +1626,7 @@ class RtlAllocPolicy {
template <typename T>
T* maybe_pod_malloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
return nullptr;
}
@@ -1635,7 +1636,7 @@ class RtlAllocPolicy {
template <typename T>
T* maybe_pod_calloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
return nullptr;
}
@@ -1646,7 +1647,7 @@ class RtlAllocPolicy {
template <typename T>
T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNewSize, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) {
return nullptr;
}
diff --git a/tools/profiler/core/memory_hooks.cpp b/tools/profiler/core/memory_hooks.cpp
@@ -10,6 +10,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
+#include "mozilla/CheckedArithmetic.h"
#include "mozilla/FastBernoulliTrial.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/JSONWriter.h"
@@ -147,7 +148,7 @@ class InfallibleAllocWithoutHooksPolicy {
template <typename T>
static T* maybe_pod_malloc(size_t aNumElems) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNumElems, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
return nullptr;
}
return (T*)gMallocTable.malloc(size);
@@ -161,7 +162,7 @@ class InfallibleAllocWithoutHooksPolicy {
template <typename T>
static T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
size_t size;
- if (MOZ_UNLIKELY(__builtin_mul_overflow(aNewSize, sizeof(T), &size))) {
+ if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) {
return nullptr;
}
return (T*)gMallocTable.realloc(aPtr, size);