Skip to content

Commit

Permalink
[UMM-62] Request for out of range block number must fail
Browse files Browse the repository at this point in the history
  • Loading branch information
rhempel committed Dec 24, 2021
1 parent e8a0f7d commit da41963
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 3 deletions.
45 changes: 42 additions & 3 deletions src/umm_malloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ static uint16_t umm_blocks(size_t size) {
* When a block removed from the free list, the space used by the free
* pointers is available for data. That's what the first calculation
* of size is doing.
*
* We don't check for the special case of (size == 0) here as this needs
* special handling in the caller depending on context. For example when we
* realloc() a block to size 0 it should simply be freed.
*
* We do NOT need to check for allocating more blocks than the heap can
* possibly hold - the allocator figures this out for us.
*
* There are only two cases left to consider:
*
* 1. (size <= body) Obviously this is just one block
* 2. (blocks > (2^15)) This should return ((2^15)) to force a
* failure when the allocator runs
*
* If the requested size is greater that 32677-2 blocks (max block index
* minus the overhead of the top and bottom bookkeeping blocks) then we
* will return an incorrectly truncated value when the result is cast to
* a uint16_t.
*/

if (size <= (sizeof(((umm_block *)0)->body))) {
Expand All @@ -143,12 +161,33 @@ static uint16_t umm_blocks(size_t size) {

/*
* If it's for more than that, then we need to figure out the number of
* additional whole blocks the size of an umm_block are required.
* additional whole blocks the size of an umm_block are required, so
* reduce the size request by the number of bytes in the body of the
* first block.
*/

size -= (sizeof(((umm_block *)0)->body));

/* NOTE WELL that we take advantage of the fact that INT16_MAX is the
* number of blocks that we can index in 15 bits :-)
*
* The below expression looks wierd, but it's right. Assuming body
* size of 4 bytes and a block size of 8 bytes:
*
* BYTES (BYTES-BODY) (BYTES-BODY-1)/BLOCKSIZE BLOCKS
* 1 n/a n/a 1
* 5 1 0 2
* 12 8 0 2
* 13 9 1 3
*/

size -= (1 + (sizeof(((umm_block *)0)->body)));
size_t blocks = (2 + ((size-1) / (UMM_BLOCKSIZE)));

if (blocks > (INT16_MAX)) {
blocks = INT16_MAX;
}

return 2 + size / (UMM_BLOCKSIZE);
return (uint16_t)blocks;
}

/* ------------------------------------------------------------------------ */
Expand Down
197 changes: 197 additions & 0 deletions test/test_TooBigMalloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#include "unity.h"

#include <umm_malloc_cfg.h>
#include <umm_malloc.h>

#include <support_umm_malloc.h>

/* Use the default DBGLOG_LEVEL and DBGLOG_FUNCTION */

#define DBGLOG_LEVEL 0

#ifdef DBGLOG_ENABLE
#include "dbglog/dbglog.h"
#endif

void setUp(void) {
umm_init();
umm_critical_depth = 0;
umm_max_critical_depth = 0;
}

void tearDown(void) {
TEST_ASSERT_LESS_OR_EQUAL(1, umm_max_critical_depth);
}

struct block_test_values Initialization_test_values[] =
{ {0, false, 1, 0, 1, 1}
, {1, true, UMM_LASTBLOCK, 0, 0, 0}
, {UMM_LASTBLOCK, false, 0, 1, 0, 0}};

#if 0
void testHeapInitialization(void) {
DBGLOG_FORCE(true, "support heapsize %08x\n", SUPPORT_UMM_MALLOC_HEAP_SIZE);

TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}
#endif

void testHeapFirstMallocMaxHeapBlocks(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

void testHeapFirstMallocMaxHeapBlocksMinus1(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-1) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

void testHeapFirstMallocMaxHeapBlocksMinus2(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-2) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

struct block_test_values MallocMaxHeapBlocksMinus3_test_values[] =
{ {0, false, 1, 0, 0, 0}
, {1, false, UMM_LASTBLOCK, 0, 0, 0}
, {UMM_LASTBLOCK, false, 0, 1, 0, 0}};

void testHeapFirstMallocMaxHeapBlocksMinus3(void) {
// This is a fairly complex test, so we will break it down
//
// First allocate the largest block possible ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE], (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-3) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksMinus3_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksMinus3_test_values)));

// Then free it ...
umm_free((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

struct block_test_values MallocMaxHeapBlocksBig_test_values[] =
{ {0, false, 1, 0, UMM_LASTBLOCK-1, UMM_LASTBLOCK-1}
, {1, false, UMM_LASTBLOCK-1, 0, 0, 0}
, {UMM_LASTBLOCK-1, true, UMM_LASTBLOCK, 1, 0, 0}
, {UMM_LASTBLOCK, false, 0, UMM_LASTBLOCK-1, 0, 0}};

struct block_test_values MallocMaxHeapBlocksBigThenSmall_test_values[] =
{ {0, false, 1, 0, 0, 0}
, {1, false, UMM_LASTBLOCK-1, 0, 0, 0}
, {UMM_LASTBLOCK-1, 0, UMM_LASTBLOCK, 1, 0, 0}
, {UMM_LASTBLOCK, false, 0, UMM_LASTBLOCK-1, 0, 0}};

void testHeapTooBigMalloc_BigThenSmallMax(void) {
// This is a fairly complex test, so we will break it down
//
// First allocate the largest block possible that leaves exactly one block free ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE], (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-4) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksBig_test_values)));

// Then allocate exactly one more block ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[UMM_LASTBLOCK-1][UMM_BLOCK_HEADER_SIZE], (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksBigThenSmall_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksBigThenSmall_test_values)));

// Then allocate exactly one more block ... which should fail
//
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksBigThenSmall_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksBigThenSmall_test_values)));

// Then free the last block ...
umm_free((void *)&test_umm_heap[UMM_LASTBLOCK-1][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksBig_test_values)));

// Then free the first block ... which should get us back to the initialized state
umm_free((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

struct block_test_values MallocMaxHeapBlocksSmall_test_values[] =
{ {0, false, 1, 0, 2, 2}
, {1, false, 2, 0, 0, 0}
, {2, true, UMM_LASTBLOCK, 1, 0, 0}
, {UMM_LASTBLOCK, false, 0, 2, 0, 0}};

struct block_test_values MallocMaxHeapBlocksSmallThenBig_test_values[] =
{ {0, false, 1, 0, 0, 0}
, {1, false, 2, 0, 0, 0}
, {2, 0, UMM_LASTBLOCK, 1, 0, 0}
, {UMM_LASTBLOCK, false, 0, 2, 0, 0}};

void testHeapTooBigMalloc_SmallThenBigMax(void) {
// This is a fairly complex test, so we will break it down
//
// First allocate the smallest block possible that leaves a large block free ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE], (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmall_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmall_test_values)));

// Then allocate the largest possible leftover block ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[2][UMM_BLOCK_HEADER_SIZE], (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-4) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmallThenBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmallThenBig_test_values)));

// Then allocate exactly one more block ... which should fail
//
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmallThenBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmallThenBig_test_values)));

// Then free the large block ...
umm_free((void *)&test_umm_heap[2][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmall_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmall_test_values)));

// Then free the small block ... which should get us back to the initialized state
umm_free((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

struct block_test_values MallocMaxHeapBlocksSmallThenBig_ReverseFreetest_values[] =
{ {0, false, 1, 0, 1, 1}
, {1, true, 2, 0, 0, 0}
, {2, 0, UMM_LASTBLOCK, 1, 0, 0}
, {UMM_LASTBLOCK, false, 0, 2, 0, 0}};

void testHeapTooBigMalloc_SmallThenBigMax_ReverseFree(void) {
// This is a fairly complex test, so we will break it down
//
// First allocate the smallest block possible that leaves a large block free ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE], (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmall_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmall_test_values)));

// Then allocate the largest possible leftover block ...
//
TEST_ASSERT_EQUAL_PTR((void *)&test_umm_heap[2][UMM_BLOCK_HEADER_SIZE], (umm_malloc((SUPPORT_UMM_MALLOC_BLOCKS-4) * UMM_BLOCK_BODY_SIZE)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmallThenBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmallThenBig_test_values)));

// Then allocate exactly one more block ... which should fail
//
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(1)));
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmallThenBig_test_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmallThenBig_test_values)));

// Then free the small block ...
umm_free((void *)&test_umm_heap[1][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(MallocMaxHeapBlocksSmallThenBig_ReverseFreetest_values, ARRAYELEMENTCOUNT(MallocMaxHeapBlocksSmallThenBig_ReverseFreetest_values)));

// Then free the large block ...
umm_free((void *)&test_umm_heap[2][UMM_BLOCK_HEADER_SIZE]);
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

void testHeapFirstMallocMaxNumBlocks_Minus1(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(UMM_FIRST_BLOCK_BODY_SIZE + ((INT16_MAX-2) * 500))));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

void testHeapFirstMallocMaxNumBlocks(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(UMM_FIRST_BLOCK_BODY_SIZE + ((INT16_MAX-1) * 500))));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

void testHeapFirstMallocMaxNumBlocks_Plus1(void) {
TEST_ASSERT_EQUAL_PTR((void *)NULL, (umm_malloc(UMM_FIRST_BLOCK_BODY_SIZE + ((INT16_MAX-0) * 500))));
TEST_ASSERT_TRUE(check_blocks(Initialization_test_values, ARRAYELEMENTCOUNT(Initialization_test_values)));
}

0 comments on commit da41963

Please sign in to comment.