Matter SDK Coverage Report
Current view: top level - lib/support - PrivateHeap.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 81.7 % 131 107
Test Date: 2025-01-17 19:00:11 Functions: 88.9 % 9 8

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *    Copyright 2019 Google Inc. All Rights Reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #include "PrivateHeap.h"
      20              : 
      21              : #include <string.h>
      22              : 
      23              : #include <lib/support/CodeUtils.h>
      24              : #include <lib/support/logging/CHIPLogging.h>
      25              : 
      26              : namespace {
      27              : 
      28              : constexpr uint32_t kInvalidHeapBlockSize = 0xFFFFFFFF;
      29              : constexpr int32_t kHeapBlockInUse        = 0x01;
      30              : constexpr int32_t kHeapBlockFree         = 0x10;
      31              : constexpr int32_t kInvalidHeaderState    = 0xff;
      32              : 
      33              : using internal::PrivateHeapBlockHeader;
      34              : 
      35              : // this makes life easier, no need to add align offsets.
      36              : static_assert(sizeof(PrivateHeapBlockHeader) % kPrivateHeapAllocationAlignment == 0, "Invalid block size.");
      37              : 
      38         1703 : uint32_t ComputeHeapBlockChecksum(const PrivateHeapBlockHeader * header)
      39              : {
      40         1703 :     uint32_t checksum = header->prevBytes;
      41         1703 :     checksum *= 31;
      42         1703 :     checksum += header->nextBytes;
      43         1703 :     checksum *= 31;
      44         1703 :     checksum += header->state;
      45         1703 :     return checksum;
      46              : }
      47              : 
      48              : // Advances the heap block to the next value
      49          633 : PrivateHeapBlockHeader * NextHeader(PrivateHeapBlockHeader * start)
      50              : {
      51          633 :     if (start->nextBytes == kInvalidHeapBlockSize)
      52              :     {
      53           27 :         return nullptr;
      54              :     }
      55              : 
      56          606 :     if (start->checksum != ComputeHeapBlockChecksum(start))
      57              :     {
      58            0 :         ChipLogError(Support, "Corrupted heap: checksum is invalid");
      59            0 :         chipDie();
      60              :     }
      61              : 
      62          606 :     return reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(start) + sizeof(PrivateHeapBlockHeader) +
      63          606 :                                                       start->nextBytes);
      64              : }
      65              : 
      66              : // Advances the heap block to the previous value
      67          103 : PrivateHeapBlockHeader * PreviousHeader(PrivateHeapBlockHeader * start)
      68              : {
      69          103 :     if (start->prevBytes == kInvalidHeapBlockSize)
      70              :     {
      71           47 :         return nullptr;
      72              :     }
      73              : 
      74           56 :     if (start->checksum != ComputeHeapBlockChecksum(start))
      75              :     {
      76            0 :         ChipLogError(Support, "Corrupted heap: checksum is invalid");
      77            0 :         chipDie();
      78              :     }
      79              : 
      80           56 :     return reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(start) - sizeof(PrivateHeapBlockHeader) -
      81           56 :                                                       start->prevBytes);
      82              : }
      83              : 
      84          649 : void ValidateHeader(const PrivateHeapBlockHeader * header)
      85              : {
      86          649 :     if (header->state != kHeapBlockFree && header->state != kHeapBlockInUse)
      87              :     {
      88            0 :         ChipLogError(Support, "Invalid header state (neither free nor in use) at %p", header);
      89            0 :         chipDie();
      90              :     }
      91              : 
      92          649 :     if (header->checksum != ComputeHeapBlockChecksum(header))
      93              :     {
      94            0 :         ChipLogError(Support, "Corrupted heap: checksum is invalid at %p", header);
      95            0 :         chipDie();
      96              :     }
      97          649 : }
      98              : 
      99              : } // namespace
     100              : 
     101            9 : extern "C" void PrivateHeapInit(void * heap, size_t size)
     102              : {
     103            9 :     if (heap == nullptr)
     104              :     {
     105            0 :         ChipLogError(Support, "Cannot initialize null heap");
     106            0 :         chipDie();
     107              :     }
     108              : 
     109            9 :     if (size < 2 * sizeof(PrivateHeapBlockHeader))
     110              :     {
     111            0 :         ChipLogError(Support, "Insufficient space in private heap");
     112            0 :         chipDie();
     113              :     }
     114              : 
     115            9 :     if (reinterpret_cast<uintptr_t>(heap) % kPrivateHeapAllocationAlignment != 0)
     116              :     {
     117            0 :         ChipLogError(Support, "Invalid alignment for private heap initialization");
     118            0 :         chipDie();
     119              :     }
     120              : 
     121            9 :     PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(heap);
     122              : 
     123            9 :     header->prevBytes = kInvalidHeapBlockSize;
     124            9 :     header->nextBytes = static_cast<uint32_t>(size - 2 * sizeof(PrivateHeapBlockHeader));
     125            9 :     header->state     = kHeapBlockFree;
     126            9 :     header->checksum  = ComputeHeapBlockChecksum(header);
     127              : 
     128            9 :     header            = NextHeader(header);
     129            9 :     header->nextBytes = kInvalidHeapBlockSize;
     130            9 :     header->prevBytes = static_cast<uint32_t>(size - 2 * sizeof(PrivateHeapBlockHeader));
     131            9 :     header->state     = kHeapBlockFree; // does not matter really
     132            9 :     header->checksum  = ComputeHeapBlockChecksum(header);
     133            9 : }
     134              : 
     135          130 : extern "C" void * PrivateHeapAlloc(void * heap, size_t size)
     136              : {
     137          130 :     PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(heap);
     138              : 
     139              :     // we allocate aligned, no matter what
     140          130 :     if (size % kPrivateHeapAllocationAlignment != 0)
     141              :     {
     142           40 :         size += kPrivateHeapAllocationAlignment - (size % kPrivateHeapAllocationAlignment);
     143              :     }
     144              : 
     145          567 :     for (; header != nullptr; header = NextHeader(header))
     146              :     {
     147          540 :         ValidateHeader(header);
     148              : 
     149          540 :         if (header->nextBytes == kInvalidHeapBlockSize)
     150              :         {
     151           27 :             continue;
     152              :         }
     153              : 
     154          513 :         if (header->state != kHeapBlockFree)
     155              :         {
     156          403 :             continue; // not free
     157              :         }
     158              : 
     159          110 :         if (header->nextBytes < size)
     160              :         {
     161            7 :             continue; // insufficient space
     162              :         }
     163              : 
     164          103 :         if (header->nextBytes - size < sizeof(PrivateHeapBlockHeader) + kPrivateHeapAllocationAlignment)
     165              :         {
     166              :             // allocate the entire block
     167           61 :             header->state    = kHeapBlockInUse;
     168           61 :             header->checksum = ComputeHeapBlockChecksum(header);
     169              :         }
     170              :         else
     171              :         {
     172              :             // splits the block into two
     173              :             //
     174              :             // +--------+       +--------+       +------+
     175              :             // | header |  ---> | middle |  ---> | next |
     176              :             // +--------+       +--------+       +------+
     177              :             //
     178           42 :             PrivateHeapBlockHeader * next = NextHeader(header);
     179           42 :             PrivateHeapBlockHeader * middle =
     180           42 :                 reinterpret_cast<PrivateHeapBlockHeader *>(reinterpret_cast<char *>(header + 1) + size);
     181              : 
     182              :             // middle is a new block
     183           42 :             middle->nextBytes = static_cast<uint32_t>(header->nextBytes - size - sizeof(PrivateHeapBlockHeader));
     184           42 :             middle->prevBytes = static_cast<uint32_t>(size);
     185           42 :             middle->state     = kHeapBlockFree;
     186           42 :             middle->checksum  = ComputeHeapBlockChecksum(middle);
     187              : 
     188              :             // fix up the header
     189           42 :             header->nextBytes = static_cast<uint32_t>(size);
     190           42 :             header->state     = kHeapBlockInUse;
     191           42 :             header->checksum  = ComputeHeapBlockChecksum(header);
     192              : 
     193              :             // fix up the final block
     194           42 :             if (next != nullptr)
     195              :             {
     196           42 :                 next->prevBytes = middle->nextBytes;
     197           42 :                 next->checksum  = ComputeHeapBlockChecksum(next);
     198              :             }
     199              :         }
     200              : 
     201              :         // we can now use the header
     202          103 :         return header + 1; // data right after the header
     203              :     }
     204              : 
     205              :     // no space found
     206           27 :     return nullptr;
     207              : }
     208              : 
     209          103 : extern "C" void PrivateHeapFree(void * ptr)
     210              : {
     211          103 :     if (ptr == nullptr)
     212              :     {
     213              :         // freeing NULL pointers is always acceptable and a noop
     214            0 :         return;
     215              :     }
     216              : 
     217          103 :     PrivateHeapBlockHeader * header =
     218              :         reinterpret_cast<PrivateHeapBlockHeader *>(static_cast<char *>(ptr) - sizeof(PrivateHeapBlockHeader));
     219              : 
     220          103 :     ValidateHeader(header);
     221          103 :     header->state    = kHeapBlockFree;
     222          103 :     header->checksum = ComputeHeapBlockChecksum(header);
     223              : 
     224              :     // Merge with previous
     225              :     //
     226              :     // +-------+                          +--------+
     227              :     // | other |  ----- nextBytes ----->  | header |
     228              :     // +-------+                          +--------+
     229              :     //
     230          103 :     PrivateHeapBlockHeader * other = PreviousHeader(header);
     231          103 :     if (other != nullptr && other->state == kHeapBlockFree && other->nextBytes != kInvalidHeapBlockSize)
     232              :     {
     233              :         // includes the free bytes in this block in the previous
     234           20 :         other->nextBytes += static_cast<uint32_t>(header->nextBytes + sizeof(PrivateHeapBlockHeader));
     235           20 :         other->checksum = ComputeHeapBlockChecksum(other);
     236           20 :         header->state   = kInvalidHeaderState;
     237           20 :         header          = other;
     238              : 
     239              :         // fixes up the next block
     240           20 :         other = NextHeader(header);
     241           20 :         if (other != nullptr)
     242              :         {
     243           20 :             other->prevBytes = header->nextBytes;
     244           20 :             other->checksum  = ComputeHeapBlockChecksum(other);
     245              :         }
     246              :     }
     247              : 
     248              :     // Merge with next
     249              :     //
     250              :     // +--------+                          +-------+
     251              :     // | header |  ----- nextBytes ----->  | other |
     252              :     // +--------+                          +-------+
     253              :     //
     254          103 :     other = NextHeader(header);
     255          103 :     if (other != nullptr && other->state == kHeapBlockFree && other->nextBytes != kInvalidHeapBlockSize)
     256              :     {
     257              :         // includes the free bytes in the next block
     258           22 :         other->state = kInvalidHeaderState;
     259           22 :         header->nextBytes += static_cast<uint32_t>(other->nextBytes + sizeof(PrivateHeapBlockHeader));
     260           22 :         header->checksum = ComputeHeapBlockChecksum(header);
     261              : 
     262              :         // fixes up the next block
     263           22 :         other = NextHeader(header);
     264           22 :         if (other != nullptr)
     265              :         {
     266           22 :             other->prevBytes = header->nextBytes;
     267           22 :             other->checksum  = ComputeHeapBlockChecksum(other);
     268              :         }
     269              :     }
     270              : }
     271              : 
     272            7 : void * PrivateHeapRealloc(void * heap, void * ptr, size_t size)
     273              : {
     274            7 :     if (ptr == nullptr)
     275              :     {
     276            1 :         return PrivateHeapAlloc(heap, size);
     277              :     }
     278              : 
     279            6 :     if (size == 0)
     280              :     {
     281            0 :         PrivateHeapFree(ptr);
     282            0 :         return nullptr;
     283              :     }
     284              : 
     285            6 :     PrivateHeapBlockHeader * header =
     286              :         reinterpret_cast<PrivateHeapBlockHeader *>(static_cast<char *>(ptr) - sizeof(PrivateHeapBlockHeader));
     287              : 
     288            6 :     ValidateHeader(header);
     289              : 
     290            6 :     if (header->nextBytes >= size)
     291              :     {
     292            3 :         return ptr; // no reallocation needed
     293              :     }
     294              : 
     295            3 :     void * largerCopy = PrivateHeapAlloc(heap, size);
     296            3 :     if (largerCopy == nullptr)
     297              :     {
     298              :         // NOTE: original is left untouched (not freed) to match realloc() libc
     299              :         // functionality
     300            2 :         return nullptr;
     301              :     }
     302              : 
     303            1 :     memcpy(largerCopy, ptr, header->nextBytes);
     304            1 :     PrivateHeapFree(ptr);
     305              : 
     306            1 :     return largerCopy;
     307              : }
     308              : 
     309            0 : extern "C" void PrivateHeapDump(void * top)
     310              : {
     311            0 :     PrivateHeapBlockHeader * header = reinterpret_cast<PrivateHeapBlockHeader *>(top);
     312              : 
     313            0 :     ChipLogProgress(Support, "========= HEAP ===========");
     314            0 :     while (header->nextBytes != kInvalidHeapBlockSize)
     315              :     {
     316            0 :         ChipLogProgress(Support, "    %td: size: %d, state: %d", reinterpret_cast<char *>(header) - reinterpret_cast<char *>(top),
     317              :                         static_cast<int>(header->nextBytes), static_cast<int>(header->state));
     318              : 
     319            0 :         header = NextHeader(header);
     320              :     }
     321            0 : }
        

Generated by: LCOV version 2.0-1