Skip to content

Fix deep/shallow copy issue in Joint Fabric#43208

Open
vijs wants to merge 1 commit intoproject-chip:masterfrom
vijs:feature/43174
Open

Fix deep/shallow copy issue in Joint Fabric#43208
vijs wants to merge 1 commit intoproject-chip:masterfrom
vijs:feature/43174

Conversation

@vijs
Copy link
Contributor

@vijs vijs commented Feb 18, 2026

Summary

Fix deep/shallow copy issue in Joint Fabric

Related issues

Addresses Issue: #43174.

Testing

Build asan-instrumented Apps

scripts/build/build_examples.py --target linux-x64-jf-admin-app-asan-clang build
scripts/build/build_examples.py --target linux-x64-jf-control-app-asan-clang build

Run Test (JFDS_2_2.py example)

./scripts/run_in_python_env.sh out/matter-repl 'scripts/tests/run_python_test.py --factory-reset --script src/python_testing/TC_JFDS_2_2.py --script-args "--string-arg jfa_server_app:out/linux-x64-jf-admin-app-asan-clang/jfa-app  --string-arg jfc_server_app:out/linux-x64-jf-control-app-asan-clang/jfc-app"'

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses a critical shallow copy issue within the Joint Fabric datastore by introducing dedicated storage for data backing CharSpan and ByteSpan objects. This prevents use-after-free errors when command payloads are deallocated. The changes are consistently applied across various data structures like GroupKeySet, Admin, and GroupInformation, ensuring that their lifecycles are managed correctly. The addition of helper functions for deep copying and storage management centralizes this logic, improving maintainability. My review includes a couple of suggestions to further simplify the code and remove redundancy.

Comment on lines +128 to +148
storage.friendlyName.resize(friendlyName.size());
if (!storage.friendlyName.empty())
{
memcpy(storage.friendlyName.data(), friendlyName.data(), storage.friendlyName.size());
destination.friendlyName = CharSpan(storage.friendlyName.data(), storage.friendlyName.size());
}
else
{
destination.friendlyName = CharSpan();
}

storage.icac.resize(icac.size());
if (!storage.icac.empty())
{
memcpy(storage.icac.data(), icac.data(), storage.icac.size());
destination.icac = ByteSpan(storage.icac.data(), storage.icac.size());
}
else
{
destination.icac = ByteSpan();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with CopyGroupKeySetWithOwnedSpans and SetGroupInformationFriendlyNameWithOwnedStorage, consider using std::vector::assign instead of resize and memcpy. This would make the code more concise and idiomatic.

    storage.friendlyName.assign(friendlyName.data(), friendlyName.data() + friendlyName.size());
    destination.friendlyName = CharSpan(storage.friendlyName.data(), storage.friendlyName.size());

    storage.icac.assign(icac.data(), icac.data() + icac.size());
    destination.icac = ByteSpan(storage.icac.data(), storage.icac.size());

Comment on lines +703 to +717
// Also drop storage for entries no longer present in the datastore list.
for (auto storageIt = mGroupKeySetStorage.begin(); storageIt != mGroupKeySetStorage.end();)
{
const bool inDatastore =
std::any_of(mGroupKeySetList.begin(), mGroupKeySetList.end(),
[&](const auto & entry) { return entry.groupKeySetID == storageIt->first; });
if (!inDatastore)
{
storageIt = mGroupKeySetStorage.erase(storageIt);
}
else
{
++storageIt;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This loop appears to be redundant. The preceding loop (lines 687-701) iterates through mGroupKeySetList and removes entries that are not present on the remote node, while also calling RemoveGroupKeySetStorage to clean up the backing storage for each removed entry. Since other functions that modify mGroupKeySetList (e.g., RemoveGroupKeySetEntry) also correctly manage the backing storage, this additional cleanup loop seems unnecessary. Removing it would simplify the code.

@github-actions
Copy link

github-actions bot commented Feb 18, 2026

PR #43208: Size comparison from c1553ce to dbb287f

Full report (34 builds for bl602, bl702, bl702l, cc13x4_26x4, cc32xx, efr32, nrfconnect, nxp, psoc6, qpg, realtek, stm32, telink)
platform target config section c1553ce dbb287f change % change
bl602 lighting-app bl602+mfd+littlefs+rpc FLASH 1108172 1108172 0 0.0
RAM 178674 178674 0 0.0
bl702 lighting-app bl702+eth FLASH 663248 663248 0 0.0
RAM 134689 134689 0 0.0
bl702+wifi FLASH 838954 838954 0 0.0
RAM 124213 124213 0 0.0
bl706+mfd+rpc+littlefs FLASH 1073182 1073182 0 0.0
RAM 117157 117157 0 0.0
bl702l contact-sensor-app bl702l+mfd+littlefs FLASH 905796 905796 0 0.0
RAM 105748 105748 0 0.0
lighting-app bl702l+mfd+littlefs FLASH 986248 986248 0 0.0
RAM 109644 109644 0 0.0
cc13x4_26x4 lighting-app LP_EM_CC1354P10_6 FLASH 777468 777468 0 0.0
RAM 103280 103280 0 0.0
lock-ftd LP_EM_CC1354P10_6 FLASH 786000 786000 0 0.0
RAM 108496 108496 0 0.0
pump-app LP_EM_CC1354P10_6 FLASH 731068 731068 0 0.0
RAM 97260 97260 0 0.0
pump-controller-app LP_EM_CC1354P10_6 FLASH 715596 715596 0 0.0
RAM 97460 97460 0 0.0
cc32xx air-purifier CC3235SF_LAUNCHXL FLASH 556508 556508 0 0.0
RAM 204448 204448 0 0.0
lock CC3235SF_LAUNCHXL FLASH 590704 590704 0 0.0
RAM 204744 204744 0 0.0
efr32 lock-app BRD4187C FLASH 969152 969144 -8 -0.0
RAM 125476 125476 0 0.0
BRD4338a FLASH 757708 757700 -8 -0.0
RAM 237712 237712 0 0.0
window-app BRD4187C FLASH 1066872 1066864 -8 -0.0
RAM 126700 126700 0 0.0
nrfconnect all-clusters-app nrf52840dk_nrf52840 FLASH 955936 955936 0 0.0
RAM 162081 162081 0 0.0
nxp contact mcxw71+release FLASH 746432 746432 0 0.0
RAM 66912 66912 0 0.0
psoc6 all-clusters cy8ckit_062s2_43012 FLASH 1701804 1701804 0 0.0
RAM 213852 213852 0 0.0
all-clusters-minimal cy8ckit_062s2_43012 FLASH 1602924 1602924 0 0.0
RAM 210740 210740 0 0.0
light cy8ckit_062s2_43012 FLASH 1468652 1468652 0 0.0
RAM 196936 196936 0 0.0
lock cy8ckit_062s2_43012 FLASH 1496788 1496788 0 0.0
RAM 224720 224720 0 0.0
qpg lighting-app qpg6200+debug FLASH 840292 840292 0 0.0
RAM 127764 127764 0 0.0
lock-app qpg6200+debug FLASH 778904 778904 0 0.0
RAM 118712 118712 0 0.0
realtek light-switch-app rtl8777g FLASH 704952 704952 0 0.0
RAM 113440 113440 0 0.0
lighting-app rtl8777g FLASH 750672 750672 0 0.0
RAM 114640 114640 0 0.0
stm32 light STM32WB5MM-DK FLASH 477280 477280 0 0.0
RAM 141280 141280 0 0.0
telink bridge-app tl7218x FLASH 714884 714884 0 0.0
RAM 93564 93564 0 0.0
light-app-ota-compress-lzma-shell-factory-data tl3218x FLASH 840238 840238 0 0.0
RAM 42856 42856 0 0.0
light-app-ota-shell-factory-data tl7218x FLASH 827830 827830 0 0.0
RAM 97312 97312 0 0.0
light-switch-app-ota-compress-lzma-factory-data tl7218x_retention FLASH 720662 720662 0 0.0
RAM 54800 54800 0 0.0
light-switch-app-ota-compress-lzma-shell-factory-data tlsr9528a FLASH 783726 783726 0 0.0
RAM 74504 74504 0 0.0
light-switch-app-ota-factory-data tl3218x_retention FLASH 725086 725086 0 0.0
RAM 33212 33212 0 0.0
lighting-app-ota-factory-data tlsr9118bdk40d FLASH 613776 613776 0 0.0
RAM 118192 118192 0 0.0
lighting-app-ota-rpc-factory-data-4mb tlsr9518adk80d FLASH 829136 829140 4 0.0
RAM 95092 95092 0 0.0

{
storage.epochKey2.clear();
destination.epochKey2.SetNull();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The if else from lines 36 to 70 all perform the same operations just on different values. These could be made into separate function passing the difference keys as parameters. Something like:
bool epochKeyCopy(storage, source.key, destination.key) {
if(source.key.IsNul()){
...
return true;
} else {
...
return false;
}

The true and false would indicate if storage was made clear or not. Or add other checks.
This would also reduce the above 34 lines into 14 lines total making it easier to read and modify at later times.

{
destination.epochStartTime2.SetNull();
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In similar comment to the one above about keys, lines 72-98 here can all be reduced to a single function for code reuse and reduction of lines of code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments