From dffb1b881c705062b70810033d4ef6e7d28134a1 Mon Sep 17 00:00:00 2001 From: Buke Po Date: Wed, 12 Jul 2017 01:28:21 +0800 Subject: [PATCH] [mbedtls] implement heap for mbedtls (#1970) * Implement heap for mbedTLS. * Add unit test for mbedTLS heap. --- src/core/Makefile.am | 2 + src/core/crypto/heap.cpp | 207 ++++++++++++ src/core/crypto/heap.hpp | 366 ++++++++++++++++++++++ src/core/crypto/mbedtls.cpp | 18 +- src/core/crypto/mbedtls.hpp | 18 -- src/core/openthread-core-default-config.h | 2 +- tests/unit/Makefile.am | 4 + tests/unit/test_heap.cpp | 179 +++++++++++ 8 files changed, 776 insertions(+), 20 deletions(-) create mode 100644 src/core/crypto/heap.cpp create mode 100644 src/core/crypto/heap.hpp create mode 100644 tests/unit/test_heap.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 6833de15a..c2f0682ed 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -127,6 +127,7 @@ SOURCES_COMMON = \ common/trickle_timer.cpp \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ + crypto/heap.cpp \ crypto/hmac_sha256.cpp \ crypto/mbedtls.cpp \ crypto/pbkdf2_cmac.cpp \ @@ -222,6 +223,7 @@ HEADERS_COMMON = \ common/trickle_timer.hpp \ crypto/aes_ccm.hpp \ crypto/aes_ecb.hpp \ + crypto/heap.hpp \ crypto/hmac_sha256.hpp \ crypto/mbedtls.hpp \ crypto/pbkdf2_cmac.h \ diff --git a/src/core/crypto/heap.cpp b/src/core/crypto/heap.cpp new file mode 100644 index 000000000..29aed49d0 --- /dev/null +++ b/src/core/crypto/heap.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements heap. + * + */ + +#include "heap.hpp" + +#include +#include + +#include "common/code_utils.hpp" + +namespace ot { +namespace Crypto { + +Heap::Heap(void) +{ + Block &super = BlockAt(kSuperBlockOffset); + super.SetSize(kSuperBlockSize); + + Block &first = BlockRight(super); + first.SetSize(kFirstBlockSize); + + Block &guard = BlockRight(first); + guard.SetSize(Block::kGuardBlockSize); + + super.SetNext(BlockOffset(first)); + first.SetNext(BlockOffset(guard)); +} + +void *Heap::CAlloc(size_t aCount, size_t aSize) +{ + void *ret = NULL; + Block *prev = NULL; + Block *curr = NULL; + uint16_t size = static_cast(aCount * aSize); + + VerifyOrExit(size); + + size += kAlignSize - 1 - kBlockRemainderSize; + size &= ~(kAlignSize - 1); + size += kBlockRemainderSize; + + prev = &BlockSuper(); + curr = &BlockNext(*prev); + + while (curr->GetSize() < size) + { + prev = curr; + curr = &BlockNext(*curr); + } + + VerifyOrExit(curr->IsFree()); + + prev->SetNext(curr->GetNext()); + + if (curr->GetSize() > size + sizeof(Block)) + { + const uint16_t newBlockSize = curr->GetSize() - size - sizeof(Block); + curr->SetSize(size); + + Block &newBlock = BlockRight(*curr); + newBlock.SetSize(newBlockSize); + newBlock.SetNext(0); + + if (prev->GetSize() < newBlockSize) + { + BlockInsert(*prev, newBlock); + } + else + { + BlockInsert(BlockSuper(), newBlock); + } + } + + curr->SetNext(0); + + memset(curr->GetPointer(), 0, size); + ret = curr->GetPointer(); + +exit: + return ret; +} + +void Heap::BlockInsert(Block &aPrev, Block &aBlock) +{ + Block *prev = &aPrev; + + for (Block *block = &BlockNext(*prev); + block->GetSize() < aBlock.GetSize(); + block = &BlockNext(*block)) + { + prev = block; + } + + aBlock.SetNext(prev->GetNext()); + prev->SetNext(BlockOffset(aBlock)); +} + +Block &Heap::BlockPrev(const Block &aBlock) +{ + Block *prev = &BlockSuper(); + + while (prev->GetNext() != BlockOffset(aBlock)) + { + prev = &BlockNext(*prev); + } + + return *prev; +} + +void Heap::Free(void *aPointer) +{ + if (aPointer == NULL) + { + return; + } + + Block &block = BlockOf(aPointer); + Block &right = BlockRight(block); + + if (IsLeftFree(block)) + { + Block *prev = &BlockSuper(); + Block *left = &BlockNext(*prev); + + for (const uint16_t offset = block.GetLeftNext(); left->GetNext() != offset; left = &BlockNext(*left)) + { + prev = left; + } + + // Remove left from free list. + prev->SetNext(left->GetNext()); + left->SetNext(0); + + if (right.IsFree()) + { + if (right.GetSize() > left->GetSize()) + { + for (const uint16_t offset = BlockOffset(right); prev->GetNext() != offset; prev = &BlockNext(*prev)); + } + else + { + prev = &BlockPrev(right); + } + + // Remove right from free list. + prev->SetNext(right.GetNext()); + right.SetNext(0); + + // Add size of right. + left->SetSize(left->GetSize() + right.GetSize() + sizeof(Block)); + } + + // Add size of current block. + left->SetSize(left->GetSize() + block.GetSize() + sizeof(Block)); + + BlockInsert(*prev, *left); + } + else + { + if (right.IsFree()) + { + Block &prev = BlockPrev(right); + prev.SetNext(right.GetNext()); + block.SetSize(block.GetSize() + right.GetSize() + sizeof(Block)); + BlockInsert(prev, block); + } + else + { + BlockInsert(BlockSuper(), block); + } + } +} + +} // namespace Crypto +} // namespace ot + diff --git a/src/core/crypto/heap.hpp b/src/core/crypto/heap.hpp new file mode 100644 index 000000000..a1896c6e4 --- /dev/null +++ b/src/core/crypto/heap.hpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2017, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for heap. + * + */ + +#ifndef OT_HEAP_HPP_ +#define OT_HEAP_HPP_ + +#include + +#include + +#include "openthread-core-config.h" +#include "utils/wrap_stdint.h" + +namespace ot { +namespace Crypto { + + +/** + * This class represents a memory block. + * + * A block is of the structure as below. + * + * +------------------------------+ + * | mSize | mMemory | mNext | + * |---------|----------| --------| + * | 2 bytes | n bytes | 2 bytes | + * +------------------------------+ + * + * Since block metadata is of 4-byte size, mSize and mNext are separated at the begining + * and end of the block to make sure the mMemory is aligned with long. + * + */ +class Block +{ + friend class Heap; + +public: + /** + * This method returns the size of this block. + * + * @returns Size of this block. + * + */ + uint16_t GetSize(void) const { + return mSize; + } + + /** + * This method updates the size of this block. + * + * @param[in] aSize Size of this block in bytes. + * + */ + void SetSize(uint16_t aSize) { + mSize = aSize; + } + + /** + * This method returns the offset of the free block after this block. + * + * @note This offset is relative to the start of the heap. + * + * @returns Offset of the next free block in bytes. + * + * @retval 0 This block is not free. + * + */ + uint16_t GetNext(void) const { + return *reinterpret_cast(reinterpret_cast(this) + sizeof(mSize) + mSize); + } + + /** + * This method updates the offset of the free block after this block. + * + * @note This offset @p aNext must be relative to the start of the heap. + * + * @param[in] aNext Offset of the next free block in bytes. + * + */ + void SetNext(uint16_t aNext) { + *reinterpret_cast(reinterpret_cast(this) + sizeof(mSize) + mSize) = aNext; + } + + /** + * This method returns the pointer to the start of the memory for user. + * + * @retval Pointer to the user memory. The pointer address is aligned to sizeof(long). + * + */ + void *GetPointer(void) { + return &mMemory; + } + + /** + * This method returns the offset of the free block after the left neighbor block. + * + * @returns Offset in bytes. + * + */ + uint16_t GetLeftNext(void) const { + return *(&mSize - 1); + } + + /** + * This method returns whether the left neighbor block is a free block. + * + * @retval true The left neighbor block is free. + * @retval false The left neighbor block is not free. + * + */ + bool IsLeftFree(void) const { + return GetLeftNext() != 0; + } + + /** + * This method returns whether the current block is a free block. + * + * @retval true The block is free. + * @retval false The block is not free. + * + */ + bool IsFree(void) const { + return mSize != kGuardBlockSize && GetNext() != 0; + } + +private: + enum + { + kGuardBlockSize = 0xffff, ///< Size value of the guard block. + }; + + uint16_t mSize; ///< Number of bytes in mMemory. + + /** + * Memory for user, with size of *mNext* to ensure size of this + * structure is equal to size of block metadata, i.e. sizeof(mSize) + sizeof(mNext) + * + */ + uint8_t mMemory[sizeof(uint16_t)]; +}; + +/** + * This class defines functionality to manipulate heap. + * + * This implementation is currently for mbedTLS. + * + * The memory is divided into blocks. The whole picture is as follows: + * + * +--------------------------------------------------------------------------+ + * | unused | super | block 1 | block 2 | ... | block n | guard | + * +----------------+------------+---------+---------+-----+---------+--------+ + * | kAlignSize - 2 | kAlignSize | 4 + s1 | 4 + s2 | ... | 4 + s4 | 2 | + * +--------------------------------------------------------------------------+ + * + */ +class Heap +{ +public: + /** + * This constructure initialize a memory heap. + * + */ + Heap(void); + + /** + * This method allocates at least @p aCount * @aSize bytes memory and initialize to zero. + * + * @param[in] aCount Number of allocate units. + * @param[in] aSize Unit size in bytes. + * + * @returns A pointer to the allocated memory. + * + * @retval NULL Indicates not enough memory. + * + */ + void *CAlloc(size_t aCount, size_t aSize); + + /** + * This method free memory pointed by @p aPointer. + * + * @param[in] aPointer A pointer to the memory to free. + * + */ + void Free(void *aPointer); + + /** + * This method returns whether the heap is clean. + * + */ + bool IsClean(void) { + const Block &super = BlockSuper(); + const Block &first = BlockRight(super); + return super.GetNext() == BlockOffset(first) && first.GetSize() == kFirstBlockSize; + } + + /** + * This method returns the capacity of this heap. + * + */ + size_t GetCapacity(void) const { + return kFirstBlockSize; + } + +private: + enum + { +#if OPENTHREAD_ENABLE_DTLS + kMemorySize = OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE, ///< Size of memory buffer (bytes). +#else + kMemorySize = OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE_NO_DTLS, ///< Size of memory buffer (bytes). +#endif + kAlignSize = sizeof(long), ///< The alignment size. + kBlockRemainderSize = kAlignSize - sizeof(uint16_t) * 2, ///< Block unit remainder size. + kSuperBlockSize = kAlignSize - sizeof(Block), ///< Super block size. + kFirstBlockSize = kMemorySize - kAlignSize * 3 + + kBlockRemainderSize, ///< First block size. + kSuperBlockOffset = kAlignSize - sizeof(uint16_t), ///< Offset of the super block. + kFirstBlockOffset = kAlignSize * 2 - sizeof(uint16_t), ///< Offset of the first block. + kGuardBlockOffset = kMemorySize - sizeof(uint16_t), ///< Offset of the guard block. + }; + + /** + * This method returns the block at offset @p aOffset. + * + * @param[in] aOffset Offset in bytes. + * + * @returns A reference to the block. + * + */ + Block &BlockAt(uint16_t aOffset) { + return *reinterpret_cast(&mMemory.m16[aOffset / 2]); + } + + /** + * This method returns the block of @p aPointer. + * + * @param[in] aPointer The pointer returned by CAlloc(). + * + * @returns A reference to the block. + * + */ + Block &BlockOf(void *aPointer) { + uint16_t offset = static_cast(reinterpret_cast(aPointer) - mMemory.m8); + offset -= sizeof(uint16_t); + return BlockAt(offset); + } + + /** + * This method returns the super block. + * + * @returns Reference to the super block. + * + */ + Block &BlockSuper(void) { + return BlockAt(kSuperBlockOffset); + } + + /** + * This method returns the free block after @p aBlock in the free block list. + * + * @param[in] aBlock A reference to the block. + * + * @returns Reference to the free block after this block. + * + */ + Block &BlockNext(const Block &aBlock) { + return BlockAt(aBlock.GetNext()); + } + + /** + * This method returns the block on the right side of @p aBlock. + * + * @param[in] aBlock A reference to the block. + * + * @returns Reference to the block on the right side. + * + */ + Block &BlockRight(const Block &aBlock) { + return BlockAt(BlockOffset(aBlock) + sizeof(Block) + aBlock.GetSize()); + } + + /** + * This method returns the free block before @p aBlock in the free block list. + * + * @returns Reference to the free block before this block. + * + */ + Block &BlockPrev(const Block &aBlock); + + /** + * This method returns whether the block on the left side of @p aBlock is free. + * + * @param[in] aBlock A reference to the block. + * + */ + bool IsLeftFree(const Block &aBlock) { + return (BlockOffset(aBlock) != kFirstBlockOffset && aBlock.IsLeftFree()); + } + + /** + * This method returns the offset of @p aBlock. + * + * @param[in] aBlock A reference to the block. + * + * @returns Offset in bytes of @p aBlock. + * + */ + uint16_t BlockOffset(const Block &aBlock) { + return static_cast(reinterpret_cast(&aBlock) - mMemory.m8); + } + + /** + * This method inserts @p aBlock into the free block list. + * + * The free block list is single linked and is sorted by size from minimal to maximum. + * + * @param[in] aPrev A reference to the block after which to place @p aBlock. + * @param[in] aBlock A reference to the block. + * + */ + void BlockInsert(Block &aPrev, Block &aBlock); + + union + { + // Make sure memory is long aligned. + long mLong[kMemorySize / sizeof(long)]; + uint8_t m8[kMemorySize]; + uint16_t m16[kMemorySize / sizeof(uint16_t)]; + } mMemory; +}; + +} // namespace Crypto +} // namespace ot + +#endif // OT_HEAP_HPP_ diff --git a/src/core/crypto/mbedtls.cpp b/src/core/crypto/mbedtls.cpp index e3eff6a2c..d5dcab7a3 100644 --- a/src/core/crypto/mbedtls.cpp +++ b/src/core/crypto/mbedtls.cpp @@ -35,12 +35,28 @@ #include "mbedtls.hpp" +#include + +#include "heap.hpp" + namespace ot { namespace Crypto { +static Heap sHeap; + +static void *CAlloc(size_t aCount, size_t aSize) +{ + return sHeap.CAlloc(aCount, aSize); +} + +static void Free(void *aPointer) +{ + sHeap.Free(aPointer); +} + MbedTls::MbedTls(void) { - mbedtls_memory_buffer_alloc_init(mMemory, sizeof(mMemory)); + mbedtls_platform_set_calloc_free(CAlloc, Free); } } // namespace Crypto diff --git a/src/core/crypto/mbedtls.hpp b/src/core/crypto/mbedtls.hpp index 54ae1140f..7d4edfaac 100644 --- a/src/core/crypto/mbedtls.hpp +++ b/src/core/crypto/mbedtls.hpp @@ -34,12 +34,6 @@ #ifndef OT_MBEDTLS_HPP_ #define OT_MBEDTLS_HPP_ -#include - -#include - -#include "openthread-core-config.h" - namespace ot { namespace Crypto { @@ -57,23 +51,11 @@ namespace Crypto { class MbedTls { public: - enum - { -#if OPENTHREAD_ENABLE_DTLS - kMemorySize = OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE, ///< Size of memory buffer (bytes). -#else - kMemorySize = OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE_NO_DTLS, ///< Size of memory buffer (bytes). -#endif - }; - /** * This constructor initializes the object. * */ MbedTls(void); - -private: - unsigned char mMemory[kMemorySize]; }; /** diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 3e8496f08..c781d00de 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -771,7 +771,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE -#define OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE (2048 * sizeof(void *)) +#define OPENTHREAD_CONFIG_MBEDTLS_HEAP_SIZE (1536 * sizeof(void *)) #endif /** diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 76300aeab..e869c9955 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -88,6 +88,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS check_PROGRAMS = \ test-aes \ test-fuzz \ + test-heap \ test-hmac-sha256 \ test-lowpan \ test-link-quality \ @@ -145,6 +146,9 @@ test_aes_SOURCES = test_platform.cpp test_aes.cpp test_fuzz_LDADD = $(COMMON_LDADD) test_fuzz_SOURCES = test_platform.cpp test_fuzz.cpp +test_heap_LDADD = $(COMMON_LDADD) +test_heap_SOURCES = test_platform.cpp test_heap.cpp + test_hmac_sha256_LDADD = $(COMMON_LDADD) test_hmac_sha256_SOURCES = test_platform.cpp test_hmac_sha256.cpp diff --git a/tests/unit/test_heap.cpp b/tests/unit/test_heap.cpp new file mode 100644 index 000000000..42cdb88b0 --- /dev/null +++ b/tests/unit/test_heap.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "core/crypto/heap.hpp" + +#include + +#include "common/debug.hpp" +#include "crypto/aes_ccm.hpp" +#include "utils/wrap_string.h" + +#include "test_platform.h" +#include "test_util.h" + +/** + * Verifies single variable allocating and freeing. + * + */ +void TestAllocateSingle(void) +{ + ot::Crypto::Heap heap; + + { + void *p = heap.CAlloc(1, 0); + VerifyOrQuit(p == NULL, "TestAllocateSingle allocate 1 x 0 byte failed!\n"); + heap.Free(p); + + p = heap.CAlloc(0, 1); + VerifyOrQuit(p == NULL, "TestAllocateSingle allocate 0 x 1 byte failed!\n"); + heap.Free(p); + } + + for (size_t size = 1; size <= heap.GetCapacity(); ++size) + { + Log("%s allocating %zu bytes...", __func__, size); + void *p = heap.CAlloc(1, size); + VerifyOrQuit(p != NULL && !heap.IsClean(), "allocating failed!\n"); + memset(p, 0xff, size); + heap.Free(p); + VerifyOrQuit(heap.IsClean(), "freeing failed!\n"); + } +} + +/** + * Verifies randomly allocating and freeing variables. + * + * @param[in] aSizeLimit The maximum allocation size. + * @param[in] aSeed The seed for generating random sizes. + * + */ +void TestAllocateRandomly(size_t aSizeLimit, unsigned int aSeed) +{ + struct Node + { + Node *mNext; + size_t mSize; + }; + + ot::Crypto::Heap heap; + Node head; + size_t nnodes = 0; + + srand(aSeed); + + Node *last = &head; + + do + { + size_t size = sizeof(Node) + rand() % aSizeLimit; + Log("TestAllocateRandomly allocating %zu bytes...", size); + last->mNext = static_cast(heap.CAlloc(1, size)); + + // No more memory for allocation. + if (last->mNext == NULL) + { + break; + } + + VerifyOrQuit(last->mNext->mNext == NULL, "TestAllocateRandomly memory not initialized to zero!\n"); + last = last->mNext; + last->mSize = size; + ++nnodes; + + // 50% probability to randomly free a node. + size_t freeIndex = rand() % (nnodes * 2); + + if (freeIndex > nnodes) + { + freeIndex /= 2; + + Node *prev = &head; + + while (freeIndex--) + { + prev = prev->mNext; + } + + Node *curr = prev->mNext; + Log("TestAllocateRandomly freeing %zu bytes...", curr->mSize); + prev->mNext = curr->mNext; + heap.Free(curr); + + if (last == curr) + { + last = prev; + } + + --nnodes; + } + } + while (true); + + last = head.mNext; + + while (last) + { + Node *next = last->mNext; + Log("TestAllocateRandomly freeing %zu bytes...", last->mSize); + heap.Free(last); + last = next; + } + + VerifyOrQuit(heap.IsClean(), "TestAllocateRandomly heap not clean after freeing all!\n"); +} + +/** + * Verifies allocating and free multiple variables. + */ +void TestAllocateMultiple(void) +{ + for (unsigned int seed = 0; seed < 10; ++seed) + { + size_t sizeLimit = (1 << seed); + Log("TestAllocateRandomly(%zu, %u)...", sizeLimit, seed); + TestAllocateRandomly(sizeLimit, seed); + } +} + +void RunTimerTests(void) +{ + TestAllocateSingle(); + TestAllocateMultiple(); +} + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + RunTimerTests(); + printf("All tests passed\n"); + return 0; +} +#endif