mirror of
https://github.com/espressif/openthread.git
synced 2026-08-22 10:29:52 +00:00
[netdata] add validation for incoming network data TLVs (#12040)
Introduces a new method `ValidateTlvs()` on `NetworkData` to perform structural validation of all TLVs within the network data. This new validation is invoked from `Leader::SetNetworkData()` when receiving new network data. If the new data fails validation, it is rejected, and the previous network data is restored. This prevents a device from accepting and propagating malformed network data, which could lead to parsing errors or undefined behavior on devices. The validation checks include: - All TLVs and sub-TLVs are within the network data buffer bounds. - Known TLV types like `PrefixTlv` and `ServiceTlv` are well-formed by calling their respective `IsValid()` methods. - Container TLVs like `BorderRouterTlv` and `HasRouteTlv` have a length that is an exact multiple of their entry size.
This commit is contained in:
@@ -43,6 +43,73 @@ RegisterLogModule("NetworkData");
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// NetworkData
|
||||
|
||||
Error NetworkData::ValidateTlvs(void) const
|
||||
{
|
||||
Error error = kErrorParse;
|
||||
const NetworkDataTlv *end = GetTlvsEnd();
|
||||
const NetworkDataTlv *tlv;
|
||||
const NetworkDataTlv *subTlv;
|
||||
const NetworkDataTlv *tlvEnd;
|
||||
|
||||
for (tlv = GetTlvsStart(); tlv < end; tlv = tlv->GetNext())
|
||||
{
|
||||
VerifyOrExit(tlv + 1 <= end);
|
||||
VerifyOrExit(tlv->GetNext() <= end);
|
||||
|
||||
tlvEnd = tlv->GetNext();
|
||||
subTlv = nullptr;
|
||||
|
||||
switch (tlv->GetType())
|
||||
{
|
||||
case NetworkDataTlv::kTypePrefix:
|
||||
VerifyOrExit(As<PrefixTlv>(tlv)->IsValid());
|
||||
subTlv = As<PrefixTlv>(tlv)->GetSubTlvs();
|
||||
break;
|
||||
case NetworkDataTlv::kTypeService:
|
||||
VerifyOrExit(As<ServiceTlv>(tlv)->IsValid());
|
||||
subTlv = As<ServiceTlv>(tlv)->GetSubTlvs();
|
||||
break;
|
||||
case NetworkDataTlv::kTypeCommissioningData:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (subTlv == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (; subTlv < tlvEnd; subTlv = subTlv->GetNext())
|
||||
{
|
||||
VerifyOrExit(subTlv + 1 <= tlvEnd);
|
||||
VerifyOrExit(subTlv->GetNext() <= tlvEnd);
|
||||
|
||||
switch (subTlv->GetType())
|
||||
{
|
||||
case NetworkDataTlv::kTypeContext:
|
||||
VerifyOrExit(As<ContextTlv>(subTlv)->IsValid());
|
||||
break;
|
||||
case NetworkDataTlv::kTypeServer:
|
||||
VerifyOrExit(As<ServerTlv>(subTlv)->IsValid());
|
||||
break;
|
||||
case NetworkDataTlv::kTypeBorderRouter:
|
||||
VerifyOrExit((As<BorderRouterTlv>(subTlv)->GetLength() % sizeof(BorderRouterEntry)) == 0);
|
||||
break;
|
||||
case NetworkDataTlv::kTypeHasRoute:
|
||||
VerifyOrExit((As<HasRouteTlv>(subTlv)->GetLength() % sizeof(HasRouteEntry)) == 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = kErrorNone;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error NetworkData::CopyNetworkData(Type aType, uint8_t *aData, uint8_t &aDataLength) const
|
||||
{
|
||||
Error error;
|
||||
|
||||
@@ -159,6 +159,18 @@ public:
|
||||
*/
|
||||
const uint8_t *GetBytes(void) const { return mTlvs; }
|
||||
|
||||
/**
|
||||
* Parses and validates all TLVs contained within the Network Data.
|
||||
*
|
||||
* Performs the following checks on all TLVs in the Network Data.
|
||||
* - Ensures correct TLV format and expected minimum length for known TLV types that can appear in Network Data.
|
||||
* - Validates sub-TLVs included in the known TLVs.
|
||||
*
|
||||
* @retval kErrorNone Successfully validated all the TLVs in the Network Data.
|
||||
* @retval kErrorParse Network Data TLVs are not well-formed.
|
||||
*/
|
||||
Error ValidateTlvs(void) const;
|
||||
|
||||
/**
|
||||
* Provides full or stable copy of the Thread Network Data.
|
||||
*
|
||||
|
||||
@@ -453,13 +453,30 @@ Error Leader::SetNetworkData(uint8_t aVersion,
|
||||
const Message &aMessage,
|
||||
const OffsetRange &aOffsetRange)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
uint16_t length = aOffsetRange.GetLength();
|
||||
Error error = kErrorNone;
|
||||
uint8_t oldData[kMaxSize];
|
||||
uint8_t oldLength;
|
||||
|
||||
VerifyOrExit(length <= kMaxSize, error = kErrorParse);
|
||||
SuccessOrExit(error = aMessage.Read(aOffsetRange.GetOffset(), GetBytes(), length));
|
||||
VerifyOrExit(aOffsetRange.GetLength() <= kMaxSize, error = kErrorParse);
|
||||
VerifyOrExit(aOffsetRange.GetEndOffset() <= aMessage.GetLength(), error = kErrorParse);
|
||||
|
||||
oldLength = sizeof(oldData);
|
||||
IgnoreError(CopyNetworkData(kFullSet, oldData, oldLength));
|
||||
|
||||
aMessage.ReadBytes(aOffsetRange, GetBytes());
|
||||
SetLength(static_cast<uint8_t>(aOffsetRange.GetLength()));
|
||||
|
||||
error = ValidateTlvs();
|
||||
|
||||
if (error != kErrorNone)
|
||||
{
|
||||
// Restores the old data back
|
||||
|
||||
memcpy(GetBytes(), oldData, oldLength);
|
||||
SetLength(oldLength);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
SetLength(static_cast<uint8_t>(length));
|
||||
mVersion = aVersion;
|
||||
mStableVersion = aStableVersion;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user