Skip to content

Commit c0e8ed8

Browse files
Removed un-necessary error, added checks on negative values in unit test, added signedness handling
1 parent 0576976 commit c0e8ed8

File tree

8 files changed

+123
-81
lines changed

8 files changed

+123
-81
lines changed

docs/ERROR_CODES.md

-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ This file was **AUTOMATICALLY** generated by
8989
| 92 | 0x5C | `CHIP_ERROR_WRONG_NODE_ID` |
9090
| 100 | 0x64 | `CHIP_ERROR_RETRANS_TABLE_FULL` |
9191
| 104 | 0x68 | `CHIP_ERROR_TRANSACTION_CANCELED` |
92-
| 106 | 0x6A | `CHIP_ERROR_UNSUPPORTED_ATTRIBUTE` |
9392
| 107 | 0x6B | `CHIP_ERROR_INVALID_SUBSCRIPTION` |
9493
| 108 | 0x6C | `CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE` |
9594
| 112 | 0x70 | `CHIP_ERROR_UNSOLICITED_MSG_NO_ORIGINATOR` |

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

+67-49
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,24 @@ 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-
4929
/// ConvertByteArrayToUInt64
5030
/// @brief Helper function to convert a byte array to a uint64_t value
5131
/// @param EmberAfDefaultAttributeValue & defaultValue
5232
/// @param len Length of the byte array
53-
/// @return uint64_t Value
33+
/// @return uint64_t or int64_t Value
5434
/// @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)
35+
template <typename Type>
36+
Type ConvertByteArrayToType(const EmberAfDefaultAttributeValue & defaultValue, uint16_t len)
5637
{
5738
if (len <= 2)
5839
{
59-
return static_cast<uint64_t>(defaultValue.defaultValue);
40+
return static_cast<Type>(defaultValue.defaultValue);
6041
}
6142
else
6243
{
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;
44+
Type sValue = 0;
45+
memcpy(&sValue, defaultValue.ptrToDefaultValue, len);
46+
return app::NumericAttributeTraits<Type>::StorageToWorking(sValue);
7047
}
7148
}
7249

@@ -76,32 +53,37 @@ uint64_t ConvertByteArrayToUInt64(const EmberAfDefaultAttributeValue & defaultVa
7653
/// @param[in] aVPair AttributeValuePairType
7754
/// @param[in] metadata EmberAfAttributeMetadata
7855
///
56+
template <typename Type>
7957
void CapAttributeID(AttributeValuePairType & aVPair, const EmberAfAttributeMetadata * metadata)
8058
{
8159
// Calculate the maximum value that can be represented with the given number of bytes
82-
uint64_t maxValue = 0;
60+
Type maxValue = 0;
8361

8462
// Check if the attribute type is signed
85-
if (IsSignedAttribute(metadata->attributeType))
63+
if (metadata->IsSignedIntegerAttribute())
8664
{
87-
maxValue = (1ULL << ((emberAfAttributeSize(metadata) * 8) - 1)) - 1;
65+
maxValue = static_cast<Type>((1ULL << (emberAfAttributeSize(metadata) * 8 - 1)) - 1);
8866
}
8967
else
9068
{
91-
maxValue = (1ULL << (emberAfAttributeSize(metadata) * 8)) - 1;
69+
maxValue = static_cast<Type>((1ULL << (emberAfAttributeSize(metadata) * 8)) - 1);
9270
}
9371

9472
// Check metadata for min and max values
95-
if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) && metadata->defaultValue.ptrToMinMaxValue)
73+
if (metadata->HasMinMax())
9674
{
9775
const EmberAfAttributeMinMaxValue * minMaxValue = metadata->defaultValue.ptrToMinMaxValue;
98-
uint64_t minVal = ConvertByteArrayToUInt64(minMaxValue->minValue, emberAfAttributeSize(metadata));
99-
uint64_t maxVal = ConvertByteArrayToUInt64(minMaxValue->maxValue, emberAfAttributeSize(metadata));
76+
Type minVal = ConvertByteArrayToType<Type>(minMaxValue->minValue, emberAfAttributeSize(metadata));
77+
Type maxVal = ConvertByteArrayToType<Type>(minMaxValue->maxValue, emberAfAttributeSize(metadata));
10078

101-
// Cap based on minValue
102-
if (minVal > aVPair.attributeValue)
79+
// Cap based on minimum value
80+
if (minVal > static_cast<Type>(aVPair.attributeValue))
10381
{
104-
aVPair.attributeValue = minVal;
82+
uint64_t sValue = 0;
83+
memcpy(&sValue, &minVal, sizeof(Type));
84+
aVPair.attributeValue = app::NumericAttributeTraits<uint64_t>::StorageToWorking(sValue);
85+
// We assume the max is >= min therefore we can return
86+
return;
10587
}
10688

10789
// Adjust maxValue if greater than the meta data's max value
@@ -112,17 +94,31 @@ void CapAttributeID(AttributeValuePairType & aVPair, const EmberAfAttributeMetad
11294
}
11395

11496
// Cap based on maximum value
115-
if (aVPair.attributeValue > maxValue)
97+
if (metadata->IsSignedIntegerAttribute())
11698
{
117-
aVPair.attributeValue = maxValue;
99+
if (static_cast<int64_t>(aVPair.attributeValue) > static_cast<int64_t>(maxValue))
100+
{
101+
uint64_t sValue = 0;
102+
memcpy(&sValue, &maxValue, sizeof(Type));
103+
aVPair.attributeValue = app::NumericAttributeTraits<uint64_t>::StorageToWorking(sValue);
104+
}
105+
}
106+
else
107+
{
108+
if (static_cast<uint64_t>(aVPair.attributeValue) > static_cast<uint64_t>(maxValue))
109+
{
110+
uint64_t sValue = 0;
111+
memcpy(&sValue, &maxValue, sizeof(Type));
112+
aVPair.attributeValue = app::NumericAttributeTraits<uint64_t>::StorageToWorking(sValue);
113+
}
118114
}
119115
}
120116

121117
/// @brief Validate the attribute exists for a given cluster
122118
/// @param[in] endpoint Endpoint ID
123119
/// @param[in] clusterID Cluster ID
124120
/// @param[in] aVPair AttributeValuePairType, will be mutated to cap the value if it is out of range
125-
/// @return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE if the attribute does not exist for a given cluster or is not scenable
121+
/// @return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) if the attribute does not exist for a given cluster or is not scenable
126122
/// @note This will allways fail for global list attributes. If we do want to make them scenable someday, we will need to
127123
/// use a different validation method.
128124
// TODO: Add check for "S" quality to determine if the attribute is scenable once suported :
@@ -133,17 +129,39 @@ CHIP_ERROR ValidateAttributePath(EndpointId endpoint, ClusterId cluster, Attribu
133129

134130
if (nullptr == metadata)
135131
{
136-
return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE;
132+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
137133
}
138134

139-
if (!IsSceneValidAttributeType(metadata->attributeType))
135+
switch (metadata->attributeType)
140136
{
141-
return CHIP_ERROR_UNSUPPORTED_ATTRIBUTE;
137+
case ZCL_INT8U_ATTRIBUTE_TYPE:
138+
CapAttributeID<uint8_t>(aVPair, metadata);
139+
break;
140+
case ZCL_INT16U_ATTRIBUTE_TYPE:
141+
CapAttributeID<uint16_t>(aVPair, metadata);
142+
break;
143+
case ZCL_INT32U_ATTRIBUTE_TYPE:
144+
CapAttributeID<uint32_t>(aVPair, metadata);
145+
break;
146+
case ZCL_INT64U_ATTRIBUTE_TYPE:
147+
CapAttributeID<uint64_t>(aVPair, metadata);
148+
break;
149+
case ZCL_INT8S_ATTRIBUTE_TYPE:
150+
CapAttributeID<int8_t>(aVPair, metadata);
151+
break;
152+
case ZCL_INT16S_ATTRIBUTE_TYPE: // fallthrough
153+
CapAttributeID<int16_t>(aVPair, metadata);
154+
break;
155+
case ZCL_INT32S_ATTRIBUTE_TYPE:
156+
CapAttributeID<int32_t>(aVPair, metadata);
157+
break;
158+
case ZCL_INT64S_ATTRIBUTE_TYPE:
159+
CapAttributeID<int64_t>(aVPair, metadata);
160+
break;
161+
default:
162+
return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
142163
}
143164

144-
// Cap value based on the attribute type size
145-
CapAttributeID(aVPair, metadata);
146-
147165
return CHIP_NO_ERROR;
148166
}
149167
} // namespace

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

-4
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, Res
7171
{
7272
resp.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
7373
}
74-
else if (CHIP_ERROR_UNSUPPORTED_ATTRIBUTE == err)
75-
{
76-
resp.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
77-
}
7874
else
7975
{
8076
resp.status = to_underlying(StatusIB(err).mStatus);

src/app/tests/TestSceneTable.cpp

+37-7
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,10 @@ void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext)
723723
memset(failBuffer, 0, fail_list.size());
724724
memset(buffer, 0, buff_span.size());
725725

726-
// Test Serialize Add of an attribute value that is greater than the mock attribute size (size of uint32_t)
727-
OOPairs[0].attributeValue = 0xFFFFFFFF'FFFFFFFF;
726+
// Test Serialize Add of an attribute value that is greater than the mock attribute max (Max int32_t value)
727+
OOPairs[0].attributeValue = 0x7FFFFFFF'FFFFFFFF;
728728

729-
// EFS to test caping of value once a variable above the mock attribute size is serialize
729+
// EFS to test caping of value once a variable above the mock attribute size is serialized
730730
app::Clusters::ScenesManagement::Structs::ExtensionFieldSet::Type extensionFieldValueCapOut;
731731
app::Clusters::ScenesManagement::Structs::ExtensionFieldSet::DecodableType extensionFieldValueCapIn;
732732

@@ -754,14 +754,44 @@ 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-
760757
// Verify that the output value is capped
761758
NL_TEST_ASSERT(aSuite, extensionFieldValueCapOut.attributeValueList[0].attributeValue == 0x00FFFFFF);
762759

763760
// Clear buffer
764-
memset(failBuffer, 0, fail_list.size());
761+
memset(buffer, 0, buff_span.size());
762+
763+
// Test Serialize Add of an attribute value that is smaller than the mock attribute min (-2)
764+
OOPairs[0].attributeValue = 0xFFFFFFFF'FFFFFFFE;
765+
766+
extensionFieldValueCapOut.clusterID = kOnOffClusterId;
767+
extensionFieldValueCapOut.attributeValueList = OOPairs;
768+
769+
/// Setup of input EFS (by temporary using the output one)
770+
buff_span = MutableByteSpan(buffer);
771+
writer.Init(buff_span);
772+
NL_TEST_ASSERT(
773+
aSuite, CHIP_NO_ERROR == app::DataModel::Encode(writer, TLV::AnonymousTag(), extensionFieldValueCapOut.attributeValueList));
774+
775+
reader.Init(buffer);
776+
extensionFieldValueCapIn.clusterID = kOnOffClusterId;
777+
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.Next());
778+
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == extensionFieldValueCapIn.attributeValueList.Decode(reader));
779+
780+
// Verify that the initial value is not capped
781+
auto iterator = extensionFieldValueCapIn.attributeValueList.begin();
782+
iterator.Next();
783+
pair = iterator.GetValue();
784+
NL_TEST_ASSERT(aSuite, pair.attributeValue == OOPairs[0].attributeValue);
785+
786+
// Verify that we cap the value to the mock attribute size when serializing
787+
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(kTestEndpoint1, extensionFieldValueCapIn, buff_span));
788+
NL_TEST_ASSERT(aSuite,
789+
CHIP_NO_ERROR == sHandler.Deserialize(kTestEndpoint1, kOnOffClusterId, buff_span, extensionFieldValueCapOut));
790+
791+
// Verify that the output value is capped to -1 (0xFFFFFFFF)
792+
NL_TEST_ASSERT(aSuite, extensionFieldValueCapOut.attributeValueList[0].attributeValue == 0xFFFFFFFF);
793+
794+
// Clear buffer
765795
memset(buffer, 0, buff_span.size());
766796
};
767797

src/app/util/attribute-metadata.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
#pragma once
1919

20+
#include <app-common/zap-generated/attribute-type.h>
2021
#include <app/util/basic-types.h>
21-
2222
#include <cstdint>
2323

2424
/**
@@ -158,6 +158,19 @@ struct EmberAfAttributeMetadata
158158
*/
159159
EmberAfAttributeMask mask;
160160

161+
/**
162+
* Check wether this attribute is signed based on its type according to the spec.
163+
*/
164+
bool IsSignedIntegerAttribute() const
165+
{
166+
return (attributeType >= ZCL_INT8S_ATTRIBUTE_TYPE && attributeType <= ZCL_INT64S_ATTRIBUTE_TYPE);
167+
}
168+
169+
/**
170+
* Check whether this attribute has a define min and max.
171+
*/
172+
bool HasMinMax() const { return mask & ATTRIBUTE_MASK_MIN_MAX; }
173+
161174
/**
162175
* Check whether this attribute is nullable.
163176
*/

src/app/util/attribute-table.cpp

+1-7
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ using namespace chip;
4141
using namespace chip::app;
4242

4343
namespace {
44-
// Zigbee spec says types between signed 8 bit and signed 64 bit
45-
bool emberAfIsTypeSigned(EmberAfAttributeType dataType)
46-
{
47-
return (dataType >= ZCL_INT8S_ATTRIBUTE_TYPE && dataType <= ZCL_INT64S_ATTRIBUTE_TYPE);
48-
}
49-
5044
/**
5145
* @brief Simple integer comparison function.
5246
* Compares two values of a known length as integers.
@@ -385,7 +379,7 @@ Status emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId at
385379
maxBytes = maxv.ptrToDefaultValue;
386380
}
387381

388-
bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType);
382+
bool isAttributeSigned = metadata->IsSignedIntegerAttribute();
389383
bool isOutOfRange = emberAfCompareValues(minBytes, data, dataLen, isAttributeSigned) == 1 ||
390384
emberAfCompareValues(maxBytes, data, dataLen, isAttributeSigned) == -1;
391385

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ uint8_t mockAttribute4[256] = {
8080
};
8181

8282
const uint8_t defaultValueData[] = { 0x01, 0x02, 0x03, 0x04 };
83-
const uint8_t minValueData[] = { 0x00, 0x00, 0x00, 0x00 };
83+
const uint8_t minValueData[] = { 0xFF, 0xFF, 0xFF, 0xFF };
8484
#if CHIP_CONFIG_BIG_ENDIAN_TARGET
8585
const uint8_t maxValueData[] = { 0x00, 0xFF, 0xFF, 0xFF }; // Equivalent, in big-endian, to 0x00FFFFFF
8686
#else
@@ -91,8 +91,8 @@ EmberAfAttributeMinMaxValue minMaxValue = { defaultValueData, minValueData, maxV
9191

9292
EmberAfAttributeMetadata mockmetadata = { .defaultValue = EmberAfDefaultOrMinMaxAttributeValue(&minMaxValue),
9393
.attributeId = 0,
94-
.size = sizeof(uint32_t),
95-
.attributeType = ZCL_INT32U_ATTRIBUTE_TYPE,
94+
.size = sizeof(int32_t),
95+
.attributeType = ZCL_INT32S_ATTRIBUTE_TYPE,
9696
.mask = ATTRIBUTE_MASK_MIN_MAX }; // namespace
9797

9898
} // namespace

src/lib/core/CHIPError.h

+1-9
Original file line numberDiff line numberDiff line change
@@ -1135,15 +1135,7 @@ using CHIP_ERROR = ::chip::ChipError;
11351135
#define CHIP_ERROR_TRANSACTION_CANCELED CHIP_CORE_ERROR(0x68)
11361136

11371137
// AVAILABLE: 0x69
1138-
1139-
/**
1140-
* @def CHIP_ERROR_UNSUPPORTED_ATTRIBUTE
1141-
*
1142-
* @brief
1143-
* The requested CHIP attribute is not supported.
1144-
*
1145-
*/
1146-
#define CHIP_ERROR_UNSUPPORTED_ATTRIBUTE CHIP_CORE_ERROR(0x6a)
1138+
// AVAILABLE: 0x6a
11471139

11481140
/**
11491141
* @def CHIP_ERROR_INVALID_SUBSCRIPTION

0 commit comments

Comments
 (0)