project-chip/connectedhomeip

[BUG] Heap-Use-After-Free (UAF) in tv-casting-app when deleting a fabric after casting

Opened this issue · 0 comments

Reproduction steps

  1. Build the tv-casting-app with AddressSanitizer (ASAN) enabled.

  2. Open Terminal 1 and start the tv-casting-app:

    $ ./tv-casting-app
  3. Open Terminal 2 and start the tv-app:

    $ ./tv-app
  4. In the tv-casting-app shell, send a cast request:

    tv-casting-app > cast request 0
  5. In the tv-app shell, respond with:

    tv-app > controller ux ok
  6. In the tv-casting-app, print the list of fabrics and delete a specific fabric:

    tv-casting-app > cast print-fabrics
    tv-casting-app > cast delete-fabric <fabricId>
  7. Observe the ASAN output in tv-casting-app indicating a heap-use-after-free issue.
    tv-casting-app UAF.txt

Summary

A heap-use-after-free (UAF) issue occurs in the tv-casting-app when the delete-fabric command is executed. After removing a fabric, the system attempts to access a freed ReadClient object.

Analysis and Description

The issue arises from a lifecycle mismatch between fabric cleanup and the ReadClient object references. The sequence of events leading to the issue is as follows:

  1. Fabric Removal (FabricTable.cpp):

    • The Delete() method of FabricTable is called, which iterates through its delegates and invokes OnFabricRemoved():

      CHIP_ERROR FabricTable::Delete(FabricIndex fabricIndex)
      {
          if (mDelegateListRoot != nullptr)
          {
              FabricTable::Delegate * delegate = mDelegateListRoot;
              while (delegate)
              {
                  FabricTable::Delegate * nextDelegate = delegate->next;
                  delegate->OnFabricRemoved(*this, fabricIndex);
                  delegate = nextDelegate;
              }
          }
      }
      
  2. ReadClient Cleanup (InteractionModelEngine.cpp):

    • The OnFabricRemoved() method loops through active ReadClient objects and calls Close():

      void InteractionModelEngine::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex)
      {
          for (auto * readClient = mpActiveReadClientList; readClient != nullptr; readClient = readClient->GetNextClient())
          {
              if (readClient->GetFabricIndex() == fabricIndex)
              {
                  readClient->Close(CHIP_ERROR_IM_FABRIC_DELETED, false);
              }
          }
      }
      
  3. ReadClient Deallocation (ReadClient.cpp):

    • The Close() method triggers the callback mpCallback.OnDone(this), marking the ReadClient for destruction:

      void ReadClient::Close(CHIP_ERROR aError, bool allowResubscription)
      {
          ...
          mpCallback.OnDone(this);
      }
      
  4. Use of Freed Memory:

    • Despite being destroyed, the ReadClient remains in the mpActiveReadClientList. Subsequent iterations in OnFabricRemoved() access the dangling pointer. Specifically, the readClient->GetNextClient() call accesses the mpNext pointer of the destroyed ReadClient object, leading to a UAF:

      ReadClient * GetNextClient() { return mpNext; }
    • This happens because GetNextClient() does not verify the validity of the mpNext pointer, which now points to a deallocated memory region.

Bug prevalence

always

GitHub hash of the SDK that was being used

04e6a68

Platform

core

Platform Version(s)

all versions

Anything else?

No response