This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/CC2652R: An dangerous issue about bdb_finding_and_binding in every version of z-stack.

Part Number: CC2652R
Other Parts Discussed in Thread: Z-STACK

Tool/software: TI-RTOS

Check the file bdb_finding_and_binding.c in every version of z-stack and search the variable bdb_FindingBindingTargetSimpleDesc.

This struct-type variable has to member, pAppInClusterList and pAppOutClusterList. They will be allocated memory buffer by ZDO_ParseSimpleDescBuf when bdb_ProcessSimpleDesc is called, but it will not be free when bdb_ProcessIEEEAddrRsp binding success. The bdb_FindingBindingTargetSimpleDesc needs to be freed by bdb_zclSimpleDescClusterListClean when bdb_ProcessIEEEAddrRsp finish.

so the function bdb_ProcessIEEEAddrRsp need to be fix like this

void bdb_ProcessIEEEAddrRsp(zdoIncomingMsg_t *pMsg)
{
  ZDO_NwkIEEEAddrResp_t *pAddrRsp = NULL;
  bdbFindingBindingRespondent_t *pCurr = NULL;

  pAddrRsp = ZDO_ParseAddrRsp( pMsg );
  
  if(pAddrRsp == NULL)
  {
    return;
  }
  
  bdb_setEpDescListToActiveEndpoint();
  
  pCurr = bdb_findRespondentNode(bdb_FindingBindingTargetSimpleDesc.EndPoint, pAddrRsp->nwkAddr);
  
  //Does the entry exist and we were waiting an IEEE addr rsp from this device?
  if((pCurr != NULL) && (pCurr->attempts > FINDING_AND_BINDING_MISSING_IEEE_ADDR))
  {
    if(pAddrRsp->status == ZSuccess )
    {
      uint8 extAddr[8]; 
      AddrMgrEntry_t entry;
      
      entry.nwkAddr = pAddrRsp->nwkAddr;
      entry.user = ADDRMGR_USER_BINDING;
      AddrMgrExtAddrSet(entry.extAddr, pAddrRsp->extAddr);
      
      //Add it as bind entry
      if(AddrMgrEntryUpdate(&entry) == FALSE)
      {
        //No space, then report F&B table full
        //If periodic was triggered, then finish it
        if(FINDING_AND_BINDING_PERIODIC_ENABLE == TRUE)                                  
        {
          bdb_FB_InitiatorCurrentCyclesNumber = 0;
          osal_stop_timerEx(bdb_TaskID, BDB_FINDING_AND_BINDING_PERIOD_TIMEOUT);
        }
        
        bdb_zclSimpleDescClusterListClean( &bdb_FindingBindingTargetSimpleDesc );
        osal_stop_timerEx( bdb_TaskID, BDB_RESPONDENT_PROCESS_TIMEOUT );
        bdb_exitFindingBindingWStatus( BDB_COMMISSIONING_FB_BINDING_TABLE_FULL );
        return;
      }

      //search for the matching clusters to be added this time as we have the IEEE addrs
      bdb_checkMatchingEndpoints(TRUE, pAddrRsp->nwkAddr, &pCurr);
      (void)extAddr;  //dummy
    }
    //Bind cannot be added if the device was not found
    pCurr->attempts = FINDING_AND_BINDING_RESPONDENT_COMPLETE;
  }
  
  //release the memory
  osal_mem_free( pAddrRsp );
  bdb_zclSimpleDescClusterListClean( &bdb_FindingBindingTargetSimpleDesc );//   SimpleDesc needs to be freed.
}

and bdb_ProcessSimpleDesc also need to be fix

void bdb_ProcessSimpleDesc( zdoIncomingMsg_t *msgPtr )
{
  zAddrType_t dstAddr;
  bdbFindingBindingRespondent_t *pCurr = NULL;
  uint8 isRespondantReadyToBeAdded = FALSE;

  bdb_setEpDescListToActiveEndpoint();
  
  if ( !(bdb_CurrEpDescriptorList->epDesc->epType & BDB_FINDING_AND_BINDING_INITIATOR )) 
  {  
    //We should not be processing these commands as we are not initiator
    return;
  }
  
  dstAddr.addr.shortAddr = BUILD_UINT16( msgPtr->asdu[1], msgPtr->asdu[2] );
  dstAddr.addrMode = Addr16Bit;
  
  bdb_zclSimpleDescClusterListClean( &bdb_FindingBindingTargetSimpleDesc ); //    make sure it has been freed.
  ZDO_ParseSimpleDescBuf( &msgPtr->asdu[4], &bdb_FindingBindingTargetSimpleDesc );
  
  pCurr = bdb_findRespondentNode(bdb_FindingBindingTargetSimpleDesc.EndPoint, dstAddr.addr.shortAddr);
  
  //Just for safety check this is a valid entry
  if(pCurr != NULL) 
  {
    uint8 extAddr[Z_EXTADDR_LEN]; 
    
    if(AddrMgrExtAddrLookup( pCurr->data.addr.shortAddr, extAddr ))
    {
      isRespondantReadyToBeAdded = TRUE;
    }
    else
    {
      //Save the simple desc to don't ask for it again
      pCurr->SimpleDescriptor = &bdb_FindingBindingTargetSimpleDesc;
    }
    (void)extAddr;  //dummy
  }
  else
  {
    //This simple desc rsp was not requested by BDB F&B
    return;
  } 
  
  bdb_checkMatchingEndpoints(isRespondantReadyToBeAdded, dstAddr.addr.shortAddr, &pCurr);
  
  //If the respondent got process complete, then release the entry
  if(pCurr->attempts == FINDING_AND_BINDING_RESPONDENT_COMPLETE)
  {
    bdb_zclSimpleDescClusterListClean( &bdb_FindingBindingTargetSimpleDesc );  
  }
}

  • Function bdb_ZclIdentifyQueryCmdInd also needs to be fix. In this function, don't change the event BDB_RESPONDENT_PROCESS_TIMEOUT and let it triggered by timing. Because there will be more than one Identify_Query_Response message received after broadcasting Identify_Query message.

  • Hi,

    Thank you for pointing out the potential memory leak and suggesting a fix.
    We will continue investigating and updating the code accordingly for future releases.


    Regards,
    Toby
  • But, the whole code for  "finding & binding" is not perfectly running. Sometimes it waste source of CPU and memory. For example, the member "SimpleDescriptor" in struct "bdbFindingBindingRespondent_t" has never be used.

    I also don't understand what is mean of these code at end of function "bdb_checkMatchingEndpoints". I don't think it can set "bdb_CurrEpDescriptorList" pointing to next Endpoint successfully, if function "bdb_setEpDescListToActiveEndpoint" is called after "bdb_checkMatchingEndpoints" called, the value of bdb_CurrEpDescriptorList will be reset. 

        if( bdbIndentifyActiveEndpoint != 0xFF )
        {
          break;
        }
        else
        {
          //If active endpoints 'all' is attempted, then process the next endpoint in 
          //the list
          bdb_CurrEpDescriptorList = bdb_CurrEpDescriptorList->nextDesc;
    
          while(bdb_CurrEpDescriptorList != NULL)
          {
            //It has to be different from 0 or reserved for Zigbee
            if((bdb_CurrEpDescriptorList->epDesc->endPoint != 0) && (bdb_CurrEpDescriptorList->epDesc->endPoint < BDB_ZIGBEE_RESERVED_ENDPOINTS_START))
            {
              break;
            }
            bdb_CurrEpDescriptorList = bdb_CurrEpDescriptorList->nextDesc;
          }
        }

  • The application code is not meant to call bdb functions directly.

    You may notice that the only two functions which call
    bdb_setEpDescListToActiveEndpoint and bdb_checkMatchingEndpoints are
    bdb_ProcessIEEEAddrRsp and bdb_ProcessSimpleDesc.
    In both cases, bdb_setEpDescListToActiveEndpoint is always called before bdb_checkMatchingEndpoints.

    As you've stated, bdb_setEpDescListToActiveEndpoint does indeed set bdb_CurrEpDescriptorList to the first endpoint that is F&B capable (it starts the search at the very beginning: bdb_HeadEpDescriptorList).
    Then bdb_checkMatchingEndpoints will loop through all of the device's endpoints, beginning at bdb_CurrEpDescriptorList.