From 5b0ac22628aa62409e7a8028a6f849c1b9aea0b7 Mon Sep 17 00:00:00 2001 From: Zibran Khan Date: Thu, 25 Jun 2026 22:35:26 +0530 Subject: [PATCH 1/2] rebind door lock user/credential info spans on copy --- .../door-lock-server/door-lock-server.h | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index 699852da219f73..49c3de9c3106fb 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -35,6 +35,9 @@ #include #include +#include +#include + #ifndef DOOR_LOCK_SERVER_ENDPOINT #define DOOR_LOCK_SERVER_ENDPOINT 1 #endif @@ -785,6 +788,26 @@ struct EmberAfPluginDoorLockCredentialInfo #if DOOR_LOCK_USE_LOCAL_BUFFER uint8_t credentialDataBuffer[DOOR_LOCK_CREDENTIAL_BUFFER_LENGTH]; EmberAfPluginDoorLockCredentialInfo() { credentialData = chip::MutableByteSpan(credentialDataBuffer); } + + // credentialData is a span into credentialDataBuffer, so the implicitly-defined copy would leave a + // copy's span pointing into the source's buffer (dangling once the source is gone). Deep-copy the + // bytes and re-bind the span to this object's own buffer. + EmberAfPluginDoorLockCredentialInfo(const EmberAfPluginDoorLockCredentialInfo & other) { *this = other; } + + EmberAfPluginDoorLockCredentialInfo & operator=(const EmberAfPluginDoorLockCredentialInfo & other) + { + status = other.status; + credentialType = other.credentialType; + creationSource = other.creationSource; + createdBy = other.createdBy; + modificationSource = other.modificationSource; + lastModifiedBy = other.lastModifiedBy; + + size_t dataLen = std::min(other.credentialData.size(), sizeof(credentialDataBuffer)); + memcpy(credentialDataBuffer, other.credentialData.data(), dataLen); + credentialData = chip::MutableByteSpan(credentialDataBuffer, dataLen); + return *this; + } #endif }; @@ -818,6 +841,35 @@ struct EmberAfPluginDoorLockUserInfo userName = chip::MutableCharSpan(nameBuffer); credentials = chip::Span(credentialsBuffer); } + + // userName and credentials are spans into nameBuffer/credentialsBuffer, so the implicitly-defined + // copy would leave a copy's spans pointing into the source's buffers (dangling once the source is + // gone). Deep-copy the contents and re-bind the spans to this object's own buffers. + EmberAfPluginDoorLockUserInfo(const EmberAfPluginDoorLockUserInfo & other) { *this = other; } + + EmberAfPluginDoorLockUserInfo & operator=(const EmberAfPluginDoorLockUserInfo & other) + { + userUniqueId = other.userUniqueId; + userStatus = other.userStatus; + userType = other.userType; + credentialRule = other.credentialRule; + creationSource = other.creationSource; + createdBy = other.createdBy; + modificationSource = other.modificationSource; + lastModifiedBy = other.lastModifiedBy; + + size_t nameLen = std::min(other.userName.size(), sizeof(nameBuffer)); + memcpy(nameBuffer, other.userName.data(), nameLen); + userName = chip::MutableCharSpan(nameBuffer, nameLen); + + size_t credentialCount = std::min(other.credentials.size(), sizeof(credentialsBuffer) / sizeof(credentialsBuffer[0])); + for (size_t i = 0; i < credentialCount; i++) + { + credentialsBuffer[i] = other.credentials[i]; + } + credentials = chip::Span(credentialsBuffer, credentialCount); + return *this; + } #endif }; From ddd3a99abe90520ca7ce55aab57d3009b6092cd1 Mon Sep 17 00:00:00 2001 From: Zibran Khan Date: Sat, 27 Jun 2026 11:36:42 +0530 Subject: [PATCH 2/2] Guard door lock copy-assignment against self-assign and empty span --- .../door-lock-server/door-lock-server.h | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index 49c3de9c3106fb..36b339030c8b19 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -796,6 +796,11 @@ struct EmberAfPluginDoorLockCredentialInfo EmberAfPluginDoorLockCredentialInfo & operator=(const EmberAfPluginDoorLockCredentialInfo & other) { + if (this == &other) + { + return *this; + } + status = other.status; credentialType = other.credentialType; creationSource = other.creationSource; @@ -804,7 +809,10 @@ struct EmberAfPluginDoorLockCredentialInfo lastModifiedBy = other.lastModifiedBy; size_t dataLen = std::min(other.credentialData.size(), sizeof(credentialDataBuffer)); - memcpy(credentialDataBuffer, other.credentialData.data(), dataLen); + if (dataLen > 0) + { + memcpy(credentialDataBuffer, other.credentialData.data(), dataLen); + } credentialData = chip::MutableByteSpan(credentialDataBuffer, dataLen); return *this; } @@ -849,6 +857,11 @@ struct EmberAfPluginDoorLockUserInfo EmberAfPluginDoorLockUserInfo & operator=(const EmberAfPluginDoorLockUserInfo & other) { + if (this == &other) + { + return *this; + } + userUniqueId = other.userUniqueId; userStatus = other.userStatus; userType = other.userType; @@ -859,7 +872,10 @@ struct EmberAfPluginDoorLockUserInfo lastModifiedBy = other.lastModifiedBy; size_t nameLen = std::min(other.userName.size(), sizeof(nameBuffer)); - memcpy(nameBuffer, other.userName.data(), nameLen); + if (nameLen > 0) + { + memcpy(nameBuffer, other.userName.data(), nameLen); + } userName = chip::MutableCharSpan(nameBuffer, nameLen); size_t credentialCount = std::min(other.credentials.size(), sizeof(credentialsBuffer) / sizeof(credentialsBuffer[0]));