[joiner] enhance string TLV input validation (#12245)

This commit introduces a new generic method `Tlv::ValidateStringValue()`
to provide a unified way of validating string values intended for use
in string-valued TLVs.

This new method checks that a given C string is a valid UTF-8 string
and that its length does not exceed the maximum length defined by the
`StringTlvType`.

The `Joiner::Start()` method is updated to use this new validation
method, which simplifies the code by replacing several explicit and
repetitive checks. This improves code clarity and maintainability by
centralizing the string validation logic.

This commit also adds missing validation for `aVendorModel` in
`Joiner::Start()`
This commit is contained in:
Abtin Keshavarzian
2025-12-26 20:10:12 -08:00
committed by GitHub
parent 045ea0636b
commit ed6235304b
3 changed files with 40 additions and 3 deletions
+13
View File
@@ -257,6 +257,19 @@ Error Tlv::AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringL
return AppendTlv(aMessage, aType, aValue, static_cast<uint8_t>(length));
}
Error Tlv::ValidateStringTlvValue(uint8_t aMaxStringLength, const char *aStringValue)
{
Error error = kErrorNone;
VerifyOrExit(aStringValue != nullptr);
VerifyOrExit(StringLength(aStringValue, aMaxStringLength + 1) <= aMaxStringLength, error = kErrorInvalidArgs);
VerifyOrExit(IsValidUtf8String(aStringValue), error = kErrorInvalidArgs);
exit:
return error;
}
template <typename UintType> Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue)
{
UintType value = BigEndian::HostSwap<UintType>(aValue);
+23
View File
@@ -42,6 +42,7 @@
#include "common/const_cast.hpp"
#include "common/encoding.hpp"
#include "common/error.hpp"
#include "common/numeric_limits.hpp"
#include "common/offset_range.hpp"
#include "common/type_traits.hpp"
@@ -633,6 +634,27 @@ public:
return AppendStringTlv(aMessage, StringTlvType::kType, StringTlvType::kMaxStringLength, aValue);
}
/**
* Validates a given string value for a simple TLV with a UTF-8 string value.
*
* The @p aValue can be `nullptr` in which case it is treated as an empty string.
*
* @tparam StringTlvType The simple TLV type for which to validate the value (must be a sub-class of
* `StringTlvInfo`).
*
* @param[in] aValue A pointer to a C string to validate.
*
* @retval kErrorNone The string value is valid for the given `StringTlvType`.
* @retval kErrorInvalidArgs The string is not a valid UTF-8 string or its length is longer than the max allowed
* length specified by `StringTlvType::kMaxStringLength`.
*/
template <typename StringTlvType> static Error ValidateStringValue(const char *aValue)
{
static_assert(StringTlvType::kMaxStringLength < NumericLimits<uint8_t>::kMax, "String TLV length is invalid");
return ValidateStringTlvValue(StringTlvType::kMaxStringLength, aValue);
}
//------------------------------------------------------------------------------------------------------------------
// Static methods for finding TLVs within a sequence of TLVs.
@@ -699,6 +721,7 @@ private:
static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue);
static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue);
static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue);
static Error ValidateStringTlvValue(uint8_t aMaxStringLength, const char *aStringValue);
template <typename UintType> static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue);
template <typename UintType> static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue);
template <typename UintType> static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue);
+4 -3
View File
@@ -120,9 +120,10 @@ Error Joiner::Start(const char *aPskd,
LogInfo("Joiner starting");
VerifyOrExit(aProvisioningUrl == nullptr || IsValidUtf8String(aProvisioningUrl), error = kErrorInvalidArgs);
VerifyOrExit(aVendorName == nullptr || IsValidUtf8String(aVendorName), error = kErrorInvalidArgs);
VerifyOrExit(aVendorSwVersion == nullptr || IsValidUtf8String(aVendorSwVersion), error = kErrorInvalidArgs);
SuccessOrExit(error = Tlv::ValidateStringValue<ProvisioningUrlTlv>(aProvisioningUrl));
SuccessOrExit(error = Tlv::ValidateStringValue<VendorNameTlv>(aVendorName));
SuccessOrExit(error = Tlv::ValidateStringValue<VendorModelTlv>(aVendorModel));
SuccessOrExit(error = Tlv::ValidateStringValue<VendorSwVersionTlv>(aVendorSwVersion));
VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
VerifyOrExit(Get<ThreadNetif>().IsUp() && Get<Mle::Mle>().GetRole() == Mle::kRoleDisabled,