1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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(void) {
memset(&global_testmalloc_statistics, 0, sizeof(global_testmalloc_statistics));
global_testmalloc_remaining_attempts = -1;
}
/** Sets the maximum number of malloc or realloc attempts 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;
}
}
|