diff options
Diffstat (limited to 'src/test/test-malloc.c')
-rw-r--r-- | src/test/test-malloc.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/test/test-malloc.c b/src/test/test-malloc.c new file mode 100644 index 00000000..d9c46995 --- /dev/null +++ b/src/test/test-malloc.c @@ -0,0 +1,178 @@ +/*====================================================================== +FILE: test-malloc.c + +SPDX-FileCopyrightText: 2018-2022, Markus Minichmayr <markus@tapkey.com> + + SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0 + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "test-malloc.h" +#include "icalerror.h" +#if !defined(MEMORY_CONSISTENCY) +#include "regression.h" +#endif + +#include <stdlib.h> +#include <string.h> + +struct testmalloc_statistics global_testmalloc_statistics; +static int global_testmalloc_remaining_attempts = -1; + +#define TESTMALLOC_MAGIC_NO 0x1234abcd +struct testmalloc_hdr { + uint32_t magic_no; + size_t size; +}; + +struct testmalloc_hdrlayout { + struct testmalloc_hdr hdr; + int data; +}; + +#define TESTMALLOC_HDR_SIZE ((size_t) &((struct testmalloc_hdrlayout*) 0)->data) + +void *test_malloc(size_t size) { + + void *block; + struct testmalloc_hdr *hdr; + + global_testmalloc_statistics.malloc_cnt++; + if (global_testmalloc_remaining_attempts == 0) { + global_testmalloc_statistics.malloc_failed_cnt++; + return NULL; + } + + block = malloc(size + TESTMALLOC_HDR_SIZE); + if (block == NULL) { + global_testmalloc_statistics.malloc_failed_cnt++; + return NULL; + } + + hdr = (struct testmalloc_hdr *)block; + hdr->magic_no = TESTMALLOC_MAGIC_NO; + hdr->size = size; + + global_testmalloc_statistics.mem_allocated_current += size; + if (global_testmalloc_statistics.mem_allocated_current > global_testmalloc_statistics.mem_allocated_max) { + global_testmalloc_statistics.mem_allocated_max = global_testmalloc_statistics.mem_allocated_current; + } + + global_testmalloc_statistics.blocks_allocated++; + + if (global_testmalloc_remaining_attempts > 0) { + global_testmalloc_remaining_attempts--; + } + + return (void*) &((struct testmalloc_hdrlayout *) hdr)->data; +} + +void *test_realloc(void *p, size_t size) { + + struct testmalloc_hdr *hdr; + size_t old_size; + + global_testmalloc_statistics.realloc_cnt++; + if (global_testmalloc_remaining_attempts == 0) { + global_testmalloc_statistics.realloc_failed_cnt++; + return NULL; + } + + if (p == NULL) { + global_testmalloc_statistics.realloc_failed_cnt++; + return NULL; + } + + hdr = (struct testmalloc_hdr *) (((uint8_t *) p) - TESTMALLOC_HDR_SIZE); + if (hdr->magic_no != TESTMALLOC_MAGIC_NO) { + global_testmalloc_statistics.realloc_failed_cnt++; + return NULL; + } + + old_size = hdr->size; + hdr->magic_no = 0; + + // cppcheck-suppress memleakOnRealloc; the mem block p passed to this function stays valid. + hdr = (struct testmalloc_hdr *)realloc(hdr, size + TESTMALLOC_HDR_SIZE); + if (hdr == NULL) { + global_testmalloc_statistics.realloc_failed_cnt++; + return NULL; + } + + hdr->magic_no = TESTMALLOC_MAGIC_NO; + hdr->size = size; + + global_testmalloc_statistics.mem_allocated_current += size - old_size; + if (global_testmalloc_statistics.mem_allocated_current > global_testmalloc_statistics.mem_allocated_max) { + global_testmalloc_statistics.mem_allocated_max = global_testmalloc_statistics.mem_allocated_current; + } + + if (global_testmalloc_remaining_attempts > 0) { + global_testmalloc_remaining_attempts--; + } + + return (void *) &((struct testmalloc_hdrlayout *)hdr)->data; +} + +void test_free(void *p) { + + struct testmalloc_hdr *hdr; + size_t old_size; + + if (p == NULL) { + return; + } + + global_testmalloc_statistics.free_cnt++; + + hdr = (struct testmalloc_hdr *) (((uint8_t *) p) - TESTMALLOC_HDR_SIZE); + + // The main objective of this check is to ensure, that only memory, that has been allocated via icalmemory is freed + // via icalmemory_free(). A side objective is to make sure, the block of memory hasn't been corrupted. + if (hdr->magic_no != TESTMALLOC_MAGIC_NO) { + + // If we end up here, then probably either of the following happened: + // * The calling code tries to free a block of memory via icalmemory_free() that has been allocated outside of + // icalmemory, e.g. via malloc(). + // * The header in front of the memory block being freed has been corrupted. + +#if !defined(MEMORY_CONSISTENCY) + ok("freed memory was allocated via icalmemory and has not been corrupted", + hdr->magic_no == TESTMALLOC_MAGIC_NO); +#endif + icalerror_assert(hdr->magic_no == TESTMALLOC_MAGIC_NO, + "freed memory was allocated via icalmemory and has been corrupted"); + global_testmalloc_statistics.free_failed_cnt++; + return; + } + + old_size = hdr->size; + hdr->magic_no = 0; + + free(hdr); + + global_testmalloc_statistics.mem_allocated_current -= old_size; + global_testmalloc_statistics.blocks_allocated--; +} + +void testmalloc_reset() { + memset(&global_testmalloc_statistics, 0, sizeof(global_testmalloc_statistics)); + global_testmalloc_remaining_attempts = -1; +} + +/** Sets the maximum number of malloc or realloc attemts that will succeed. If +* the number is negative, no limit will be applied. */ +void testmalloc_set_max_successful_allocs(int n) { + global_testmalloc_remaining_attempts = n; +} + +void testmalloc_get_statistics(struct testmalloc_statistics *statistics) { + + if (statistics) { + *statistics = global_testmalloc_statistics; + } +} |