Skip to content

Commit 0576976

Browse files
Added type checks and proper size check based on min max if they are existing
1 parent 4f14a77 commit 0576976

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed

src/app/clusters/scenes-server/SceneHandlerImpl.cpp

+83-4
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,92 @@ namespace {
2626
using ConcreteAttributePath = app::ConcreteAttributePath;
2727
using AttributeValuePairType = app::Clusters::ScenesManagement::Structs::AttributeValuePair::Type;
2828

29+
/// IsAttributeTypeValid
30+
/// @brief Check if the attribute type is valid for a scene extension field value according to the spec.
31+
/// @param type Type of the attribute's metadata
32+
/// @return
33+
bool IsSceneValidAttributeType(EmberAfAttributeType type)
34+
{
35+
return (type == ZCL_INT8U_ATTRIBUTE_TYPE || type == ZCL_INT16U_ATTRIBUTE_TYPE || type == ZCL_INT32U_ATTRIBUTE_TYPE ||
36+
type == ZCL_INT64U_ATTRIBUTE_TYPE || type == ZCL_INT8S_ATTRIBUTE_TYPE || type == ZCL_INT16S_ATTRIBUTE_TYPE ||
37+
type == ZCL_INT32S_ATTRIBUTE_TYPE || type == ZCL_INT64S_ATTRIBUTE_TYPE);
38+
}
39+
40+
/// IsSignedAttribute
41+
/// @brief Compare the attribute type to the signed attribute types in ZCL and return true if it is signed.
42+
/// @param attributeType metadata's attribute type
43+
/// @return
44+
bool IsSignedAttribute(EmberAfAttributeType attributeType)
45+
{
46+
return (attributeType >= ZCL_INT8S_ATTRIBUTE_TYPE && attributeType <= ZCL_INT64S_ATTRIBUTE_TYPE);
47+
}
48+
49+
/// ConvertByteArrayToUInt64
50+
/// @brief Helper function to convert a byte array to a uint64_t value
51+
/// @param EmberAfDefaultAttributeValue & defaultValue
52+
/// @param len Length of the byte array
53+
/// @return uint64_t Value
54+
/// @note The attribute table supports 8 bytes only for unsigned integers, 4 bytes is the maximum for signed integers
55+
uint64_t ConvertByteArrayToUInt64(const EmberAfDefaultAttributeValue & defaultValue, uint16_t len)
56+
{
57+
if (len <= 2)
58+
{
59+
return static_cast<uint64_t>(defaultValue.defaultValue);
60+
}
61+
else
62+
{
63+
uint64_t result = 0;
64+
const uint8_t * val = defaultValue.ptrToDefaultValue;
65+
for (size_t i = 0; i < len; i++)
66+
{
67+
result = (result << 8) | val[(CHIP_CONFIG_BIG_ENDIAN_TARGET ? i : (len - 1) - i)];
68+
}
69+
return result;
70+
}
71+
}
72+
2973
/// CapAttributeID
30-
/// Cap the attribute value based on the attribute type size
74+
/// Cap the attribute value based on the attribute's min and max if they are defined,
75+
/// or based on the attribute's size if they are not.
3176
/// @param[in] aVPair AttributeValuePairType
3277
/// @param[in] metadata EmberAfAttributeMetadata
3378
///
3479
void CapAttributeID(AttributeValuePairType & aVPair, const EmberAfAttributeMetadata * metadata)
3580
{
3681
// Calculate the maximum value that can be represented with the given number of bytes
37-
uint64_t maxValue = (1ULL << (metadata->size * 8)) - 1;
82+
uint64_t maxValue = 0;
3883

39-
// If the attribute ID is greater than the maximum value, cap it
84+
// Check if the attribute type is signed
85+
if (IsSignedAttribute(metadata->attributeType))
86+
{
87+
maxValue = (1ULL << ((emberAfAttributeSize(metadata) * 8) - 1)) - 1;
88+
}
89+
else
90+
{
91+
maxValue = (1ULL << (emberAfAttributeSize(metadata) * 8)) - 1;
92+
}
93+
94+
// Check metadata for min and max values
95+
if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) && metadata->defaultValue.ptrToMinMaxValue)
96+
{
97+
const EmberAfAttributeMinMaxValue * minMaxValue = metadata->defaultValue.ptrToMinMaxValue;
98+
uint64_t minVal = ConvertByteArrayToUInt64(minMaxValue->minValue, emberAfAttributeSize(metadata));
99+
uint64_t maxVal = ConvertByteArrayToUInt64(minMaxValue->maxValue, emberAfAttributeSize(metadata));
100+
101+
// Cap based on minValue
102+
if (minVal > aVPair.attributeValue)
103+
{
104+
aVPair.attributeValue = minVal;
105+
}
106+
107+
// Adjust maxValue if greater than the meta data's max value
108+
if (maxVal < maxValue)
109+
{
110+
maxValue = maxVal;
111+
}
112+
}
113+
114+
// Cap based on maximum value
40115
if (aVPair.attributeValue > maxValue)
41116
{
42117
aVPair.attributeValue = maxValue;
@@ -50,7 +125,6 @@ void CapAttributeID(AttributeValuePairType & aVPair, const EmberAfAttributeMetad
50125
/// @return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE if the attribute does not exist for a given cluster or is not scenable
51126
/// @note This will allways fail for global list attributes. If we do want to make them scenable someday, we will need to
52127
/// use a different validation method.
53-
// TODO: Assess if (and how) we also want to throw an error if the attribute value is out of range
54128
// TODO: Add check for "S" quality to determine if the attribute is scenable once suported :
55129
// https://github.com/project-chip/connectedhomeip/issues/24177
56130
CHIP_ERROR ValidateAttributePath(EndpointId endpoint, ClusterId cluster, AttributeValuePairType & aVPair)
@@ -62,6 +136,11 @@ CHIP_ERROR ValidateAttributePath(EndpointId endpoint, ClusterId cluster, Attribu
62136
return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE;
63137
}
64138

139+
if (!IsSceneValidAttributeType(metadata->attributeType))
140+
{
141+
return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE;
142+
}
143+
65144
// Cap value based on the attribute type size
66145
CapAttributeID(aVPair, metadata);
67146

src/app/tests/TestSceneTable.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -754,8 +754,11 @@ void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext)
754754
NL_TEST_ASSERT(aSuite,
755755
CHIP_NO_ERROR == sHandler.Deserialize(kTestEndpoint1, kOnOffClusterId, buff_span, extensionFieldValueCapOut));
756756

757+
printf("extensionFieldValueCapOut.attributeValueList[0].attributeValue = 0x%016" PRIX64 "\n",
758+
extensionFieldValueCapOut.attributeValueList[0].attributeValue);
759+
757760
// Verify that the output value is capped
758-
NL_TEST_ASSERT(aSuite, extensionFieldValueCapOut.attributeValueList[0].attributeValue == 0x00000000'FFFFFFFF);
761+
NL_TEST_ASSERT(aSuite, extensionFieldValueCapOut.attributeValueList[0].attributeValue == 0x00FFFFFF);
759762

760763
// Clear buffer
761764
memset(failBuffer, 0, fail_list.size());

src/app/util/mock/attribute-storage.cpp

+14-3
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,22 @@ uint8_t mockAttribute4[256] = {
7878
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
7979
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
8080
};
81-
EmberAfAttributeMetadata mockmetadata = { .defaultValue = EmberAfDefaultOrMinMaxAttributeValue(static_cast<uint32_t>(0)),
81+
82+
const uint8_t defaultValueData[] = { 0x01, 0x02, 0x03, 0x04 };
83+
const uint8_t minValueData[] = { 0x00, 0x00, 0x00, 0x00 };
84+
#if CHIP_CONFIG_BIG_ENDIAN_TARGET
85+
const uint8_t maxValueData[] = { 0x00, 0xFF, 0xFF, 0xFF }; // Equivalent, in big-endian, to 0x00FFFFFF
86+
#else
87+
const uint8_t maxValueData[] = { 0xFF, 0xFF, 0xFF, 0x00 }; // Equivalent, in little-endian, to 0x00FFFFFF
88+
#endif
89+
90+
EmberAfAttributeMinMaxValue minMaxValue = { defaultValueData, minValueData, maxValueData };
91+
92+
EmberAfAttributeMetadata mockmetadata = { .defaultValue = EmberAfDefaultOrMinMaxAttributeValue(&minMaxValue),
8293
.attributeId = 0,
8394
.size = sizeof(uint32_t),
84-
.attributeType = 0,
85-
.mask = 0 }; // namespace
95+
.attributeType = ZCL_INT32U_ATTRIBUTE_TYPE,
96+
.mask = ATTRIBUTE_MASK_MIN_MAX }; // namespace
8697

8798
} // namespace
8899

0 commit comments

Comments
 (0)