tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mjs/public/Utility.h | 9+++++----
Mjs/src/ds/LifoAlloc.h | 7++++---
Mjs/src/jit/BaselineBailouts.cpp | 3++-
Mjs/src/jit/CodeGenerator.cpp | 7++++---
Mjs/src/jit/IonAnalysis.cpp | 42++++++++++++++++++++++--------------------
Mjs/src/jit/JitAllocPolicy.h | 7++++---
Mjs/src/jit/RangeAnalysis.cpp | 18+++++++++---------
Djs/src/util/CheckedArithmetic.h | 92-------------------------------------------------------------------------------
Mjs/src/vm/BigIntType.cpp | 8++++----
Mjs/src/vm/Interpreter-inl.h | 5+++--
Mmemory/mozalloc/mozalloc.h | 5+++--
Mmemory/replace/dmd/DMD.cpp | 5+++--
Mmfbt/AllocPolicy.h | 5+++--
Amfbt/CheckedArithmetic.h | 31+++++++++++++++++++++++++++++++
Mmfbt/CheckedInt.h | 9+++++----
Mmfbt/Saturate.h | 30+++---------------------------
Mmfbt/Vector.h | 5+++--
Mmfbt/moz.build | 1+
Mmfbt/tests/TestBufferList.cpp | 3++-
Mmfbt/tests/TestSegmentedVector.cpp | 3++-
Mmozglue/misc/NativeNt.h | 7++++---
Mtools/profiler/core/memory_hooks.cpp | 5+++--
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);