mirror of
https://github.com/espressif/openthread.git
synced 2026-08-08 19:57:46 +00:00
[num-utils] add SafeMultiply() for overflow-safe multiplication (#12220)
This commit introduces `SafeMultiply()` in `num_utils.hpp` as a centralized and safe way to multiply two unsigned integers while checking for overflow. It updates `Coap::TxParameters::IsValid()` to use this new helper for validating `TxParameters`, replacing a less robust local `Multiply` implementation. It also updates `Heap::CAlloc()` to use this function for safely calculating the total allocation size. Unit tests are updated to verify `SafeMultiply()` implementation.
This commit is contained in:
+29
-29
@@ -1631,42 +1631,42 @@ void ResponsesQueue::HandleTimer(void)
|
||||
mTimer.FireAt(nextDequeueTime);
|
||||
}
|
||||
|
||||
/// Return product of @p aValueA and @p aValueB if no overflow otherwise 0.
|
||||
static uint32_t Multiply(uint32_t aValueA, uint32_t aValueB)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
|
||||
VerifyOrExit(aValueA);
|
||||
|
||||
result = aValueA * aValueB;
|
||||
result = (result / aValueA == aValueB) ? result : 0;
|
||||
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TxParameters::IsValid(void) const
|
||||
{
|
||||
bool rval = false;
|
||||
bool isValid = false;
|
||||
uint32_t duration;
|
||||
uint32_t retryFactor;
|
||||
|
||||
// support fire and forget requests
|
||||
if (mAckTimeout == 0)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
else if ((mAckRandomFactorDenominator > 0) && (mAckRandomFactorNumerator >= mAckRandomFactorDenominator) &&
|
||||
(mAckTimeout >= OT_COAP_MIN_ACK_TIMEOUT) && (mMaxRetransmit <= OT_COAP_MAX_RETRANSMIT))
|
||||
{
|
||||
// Calculate exchange lifetime step by step and verify no overflow.
|
||||
uint32_t tmp = Multiply(mAckTimeout, (1U << (mMaxRetransmit + 1)) - 1);
|
||||
|
||||
tmp = Multiply(tmp, mAckRandomFactorNumerator);
|
||||
tmp /= mAckRandomFactorDenominator;
|
||||
|
||||
rval = (tmp != 0 && (tmp + mAckTimeout + 2 * kDefaultMaxLatency) > tmp);
|
||||
// Support fire and forget requests
|
||||
isValid = true;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
return rval;
|
||||
VerifyOrExit(mAckRandomFactorDenominator > 0);
|
||||
VerifyOrExit(mAckRandomFactorNumerator >= mAckRandomFactorDenominator);
|
||||
VerifyOrExit(mAckTimeout >= kMinAckTimeout);
|
||||
VerifyOrExit(mMaxRetransmit <= kMaxRetransmit);
|
||||
|
||||
// Calculate exchange lifetime max duration step by step and verify no overflow.
|
||||
|
||||
static_assert(kMaxRetransmit < 31, "kMaxRetransmit is not valid");
|
||||
|
||||
retryFactor = static_cast<uint32_t>((1U << (mMaxRetransmit + 1)) - 1);
|
||||
SuccessOrExit(SafeMultiply<uint32_t>(mAckTimeout, retryFactor, duration));
|
||||
|
||||
SuccessOrExit(SafeMultiply<uint32_t>(duration, mAckRandomFactorNumerator, duration));
|
||||
duration /= mAckRandomFactorDenominator;
|
||||
|
||||
VerifyOrExit(duration > 0);
|
||||
VerifyOrExit(CanAddSafely<uint32_t>(mAckTimeout, 2 * kDefaultMaxLatency));
|
||||
VerifyOrExit(CanAddSafely<uint32_t>(duration, mAckTimeout + 2 * kDefaultMaxLatency));
|
||||
|
||||
isValid = true;
|
||||
|
||||
exit:
|
||||
return isValid;
|
||||
}
|
||||
|
||||
uint32_t TxParameters::CalculateInitialRetransmissionTimeout(void) const
|
||||
|
||||
@@ -128,6 +128,8 @@ private:
|
||||
static constexpr uint8_t kDefaultAckRandomFactorDenominator = 2;
|
||||
static constexpr uint8_t kDefaultMaxRetransmit = 4;
|
||||
static constexpr uint32_t kDefaultMaxLatency = 100000; // in msec
|
||||
static constexpr uint8_t kMaxRetransmit = OT_COAP_MAX_RETRANSMIT;
|
||||
static constexpr uint32_t kMinAckTimeout = OT_COAP_MIN_ACK_TIMEOUT;
|
||||
|
||||
uint32_t CalculateInitialRetransmissionTimeout(void) const;
|
||||
uint32_t CalculateExchangeLifetime(void) const;
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#ifndef NUM_UTILS_HPP_
|
||||
#define NUM_UTILS_HPP_
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/error.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "common/type_traits.hpp"
|
||||
|
||||
@@ -229,6 +231,37 @@ template <> inline int ThreeWayCompare(bool aFirst, bool aSecond)
|
||||
return (aFirst == aSecond) ? 0 : (aFirst ? 1 : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely multiplies two unsigned integers and checks for overflow.
|
||||
*
|
||||
* @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`).
|
||||
*
|
||||
* @param[in] aFirstValue The first operand in the multiplication.
|
||||
* @param[in] aSecondValue The second operand in the multiplication.
|
||||
* @param[out] aResult A reference to return the multiplication result.
|
||||
*
|
||||
* @retval kErrorNone If the multiplication was performed safely without overflow. @p aResult is updated.
|
||||
* @retval kErrorInvalidArgs If the multiplication would result in an overflow.
|
||||
*/
|
||||
template <typename UintType> inline Error SafeMultiply(UintType aFirstValue, UintType aSecondValue, UintType &aResult)
|
||||
{
|
||||
static_assert(TypeTraits::IsUint<UintType>::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)");
|
||||
|
||||
Error error = kErrorNone;
|
||||
|
||||
if (aFirstValue == 0 || aSecondValue == 0)
|
||||
{
|
||||
aResult = 0;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
aResult = aFirstValue * aSecondValue;
|
||||
VerifyOrExit(aResult / aFirstValue == aSecondValue, error = kErrorInvalidArgs);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* This template function divides two numbers and rounds the result to the closest integer.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -66,7 +67,6 @@ void *Heap::CAlloc(size_t aCount, size_t aSize)
|
||||
void *ret = nullptr;
|
||||
Block *prev = nullptr;
|
||||
Block *curr = nullptr;
|
||||
size_t totalSize;
|
||||
uint16_t size;
|
||||
|
||||
// Verify that the requested allocation size will not cause an overflow.
|
||||
@@ -79,12 +79,10 @@ void *Heap::CAlloc(size_t aCount, size_t aSize)
|
||||
VerifyOrExit(aCount <= NumericLimits<uint16_t>::kMax);
|
||||
VerifyOrExit(aSize <= NumericLimits<uint16_t>::kMax);
|
||||
|
||||
totalSize = aCount * aSize;
|
||||
VerifyOrExit(totalSize <= NumericLimits<uint16_t>::kMax - kTotalSizeGuard);
|
||||
SuccessOrExit(SafeMultiply<uint16_t>(static_cast<uint16_t>(aCount), static_cast<uint16_t>(aSize), size));
|
||||
|
||||
size = static_cast<uint16_t>(totalSize);
|
||||
|
||||
VerifyOrExit(size);
|
||||
VerifyOrExit(size > 0);
|
||||
VerifyOrExit(size <= NumericLimits<uint16_t>::kMax - kTotalSizeGuard);
|
||||
|
||||
size += kAlignSize - 1 - kBlockRemainderSize;
|
||||
size &= ~(kAlignSize - 1);
|
||||
|
||||
@@ -151,6 +151,41 @@ void TestNumUtils(void)
|
||||
VerifyOrQuit(ThreeWayCompare<bool>(true, false) > 0);
|
||||
VerifyOrQuit(ThreeWayCompare<bool>(false, true) < 0);
|
||||
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(0, 0, u16));
|
||||
VerifyOrQuit(u16 == 0);
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(0, 0xffff, u16));
|
||||
VerifyOrQuit(u16 == 0);
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(0xffff, 0, u16));
|
||||
VerifyOrQuit(u16 == 0);
|
||||
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(1, 0xffff, u16));
|
||||
VerifyOrQuit(u16 == 0xffff);
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(0xffff, 1, u16));
|
||||
VerifyOrQuit(u16 == 0xffff);
|
||||
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(256, 255, u16));
|
||||
VerifyOrQuit(u16 == 65280);
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(255, 256, u16));
|
||||
VerifyOrQuit(u16 == 65280);
|
||||
|
||||
VerifyOrQuit(SafeMultiply<uint16_t>(256, 256, u16) == kErrorInvalidArgs);
|
||||
|
||||
for (uint16_t num = 2; num < 255; num++)
|
||||
{
|
||||
uint16_t div = 0xffff / num;
|
||||
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(num, div, u16));
|
||||
VerifyOrQuit(u16 == num * div);
|
||||
SuccessOrQuit(SafeMultiply<uint16_t>(div, num, u16));
|
||||
VerifyOrQuit(u16 == num * div);
|
||||
|
||||
VerifyOrQuit(SafeMultiply<uint16_t>(num, div + 1, u16) == kErrorInvalidArgs);
|
||||
VerifyOrQuit(SafeMultiply<uint16_t>(div + 1, num, u16) == kErrorInvalidArgs);
|
||||
|
||||
VerifyOrQuit(SafeMultiply<uint16_t>(num + 1, div, u16) == kErrorInvalidArgs);
|
||||
VerifyOrQuit(SafeMultiply<uint16_t>(div, num + 1, u16) == kErrorInvalidArgs);
|
||||
}
|
||||
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(2, 1) == 2);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(1, 3) == 0);
|
||||
VerifyOrQuit(DivideAndRoundToClosest<uint8_t>(1, 2) == 1);
|
||||
|
||||
Reference in New Issue
Block a user