Skip to content

Commit ee2da15

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

File tree

3 files changed

+109
-16
lines changed

3 files changed

+109
-16
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

+22-11
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,31 @@ bool mockAttribute1 = true;
6969
int16_t mockAttribute2 = 42;
7070
uint64_t mockAttribute3 = 0xdeadbeef0000cafe;
7171
uint8_t mockAttribute4[256] = {
72-
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,
73-
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,
74-
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,
75-
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,
76-
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,
77-
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,
78-
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,
79-
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,
72+
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,
73+
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,
74+
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,
75+
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,
76+
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,
77+
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,
78+
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,
79+
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)