From 0b0930f1b83526d04c7daef3b31cfd9614c72acc Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 16 Oct 2025 09:13:27 +0200 Subject: [PATCH] [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. --- src/core/thread/network_data.cpp | 67 +++++++++++++++++++++++++ src/core/thread/network_data.hpp | 12 +++++ src/core/thread/network_data_leader.cpp | 27 ++++++++-- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 66b2323dd..e90daf2a3 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -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(tlv)->IsValid()); + subTlv = As(tlv)->GetSubTlvs(); + break; + case NetworkDataTlv::kTypeService: + VerifyOrExit(As(tlv)->IsValid()); + subTlv = As(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(subTlv)->IsValid()); + break; + case NetworkDataTlv::kTypeServer: + VerifyOrExit(As(subTlv)->IsValid()); + break; + case NetworkDataTlv::kTypeBorderRouter: + VerifyOrExit((As(subTlv)->GetLength() % sizeof(BorderRouterEntry)) == 0); + break; + case NetworkDataTlv::kTypeHasRoute: + VerifyOrExit((As(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; diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp index ed3b63bed..a0a8f36bd 100644 --- a/src/core/thread/network_data.hpp +++ b/src/core/thread/network_data.hpp @@ -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. * diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index 9bf376628..ab5a60eef 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -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(aOffsetRange.GetLength())); + + error = ValidateTlvs(); + + if (error != kErrorNone) + { + // Restores the old data back + + memcpy(GetBytes(), oldData, oldLength); + SetLength(oldLength); + ExitNow(); + } - SetLength(static_cast(length)); mVersion = aVersion; mStableVersion = aStableVersion;