/src/nanopb/tests/build/common/malloc_wrappers.c
Line | Count | Source |
1 | | /* The wrapper functions in this file work like regular malloc() and free(), |
2 | | * but store check values before and after the allocation. This helps to catch |
3 | | * any buffer overrun errors in the test cases. |
4 | | * |
5 | | * This is done to help testing on embedded targets, where regular valgrind |
6 | | * and other systems are not necessarily available. |
7 | | */ |
8 | | |
9 | | #include "malloc_wrappers.h" |
10 | | #include <stdint.h> |
11 | | #include <stdio.h> |
12 | | #include <assert.h> |
13 | | #include <string.h> |
14 | | |
15 | 110M | #define GUARD_SIZE (sizeof(size_t)*3) |
16 | 229M | #define PREFIX_SIZE (sizeof(size_t)*2) |
17 | 56.3M | #define CHECK1 ((size_t)0xDEADBEEF) |
18 | 2.45M | #define CHECK2 ((size_t)0x600DCAFE) |
19 | | |
20 | | #ifndef MAX_ALLOC_BYTES |
21 | | #define MAX_ALLOC_BYTES 16*1024*1024 |
22 | | #endif |
23 | | |
24 | | #ifndef DEBUG_MALLOC |
25 | 58.7M | #define DEBUG_MALLOC 0 |
26 | | #endif |
27 | | |
28 | | static size_t g_alloc_count = 0; |
29 | | static size_t g_alloc_bytes = 0; |
30 | | static size_t g_max_alloc_bytes = MAX_ALLOC_BYTES; |
31 | | |
32 | | #ifdef LLVMFUZZER |
33 | | /* LLVM libsanitizer has a realloc() implementation that always copies |
34 | | * the whole memory block, even if there would be space to expand it in |
35 | | * place. This gets pretty slow when fuzzing, so this wrapper limits the |
36 | | * realloc() calls by rounding allocation size upwards. Real world |
37 | | * realloc() implementations are hopefully smarter. */ |
38 | | static size_t round_blocksize(size_t size) |
39 | 110M | { |
40 | 110M | if (size < 256) |
41 | 9.34M | { |
42 | 9.34M | return size; |
43 | 9.34M | } |
44 | 100M | else |
45 | 100M | { |
46 | 100M | return (size + 1023) / 1024 * 1024; |
47 | 100M | } |
48 | 110M | } |
49 | | #else |
50 | | static size_t round_blocksize(size_t size) |
51 | | { |
52 | | return size; |
53 | | } |
54 | | #endif |
55 | | |
56 | | /* Allocate memory and place check values before and after. */ |
57 | | void* malloc_with_check(size_t size) |
58 | 2.45M | { |
59 | 2.45M | const size_t check2 = CHECK2; |
60 | 2.45M | char *buf = NULL; |
61 | | |
62 | 2.45M | if (size <= g_max_alloc_bytes - g_alloc_bytes) |
63 | 2.45M | { |
64 | 2.45M | buf = malloc(round_blocksize(size + GUARD_SIZE)); |
65 | 2.45M | } |
66 | | |
67 | 2.45M | if (buf) |
68 | 2.45M | { |
69 | 2.45M | ((size_t*)buf)[0] = size; |
70 | 2.45M | ((size_t*)buf)[1] = CHECK1; |
71 | 2.45M | memcpy(buf + PREFIX_SIZE + size, &check2, sizeof(check2)); |
72 | | |
73 | 2.45M | g_alloc_count++; |
74 | 2.45M | g_alloc_bytes += size; |
75 | 2.45M | if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size); |
76 | 2.45M | return buf + PREFIX_SIZE; |
77 | 2.45M | } |
78 | 189 | else |
79 | 189 | { |
80 | 189 | if (DEBUG_MALLOC) fprintf(stderr, "malloc(%u) failed\n", (unsigned)size); |
81 | 189 | return NULL; |
82 | 189 | } |
83 | 2.45M | } |
84 | | |
85 | | /* Free memory allocated with malloc_with_check() and do the checks. */ |
86 | | void free_with_check(void *mem) |
87 | 9.29M | { |
88 | 9.29M | if (mem) |
89 | 2.45M | { |
90 | 2.45M | char *buf = (char*)mem - PREFIX_SIZE; |
91 | 2.45M | size_t check2 = 0; |
92 | 2.45M | size_t size = ((size_t*)buf)[0]; |
93 | 2.45M | if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size); |
94 | 2.45M | assert(((size_t*)buf)[1] == CHECK1); |
95 | 2.45M | memcpy(&check2, buf + PREFIX_SIZE + size, sizeof(check2)); |
96 | 2.45M | assert(check2 == CHECK2); |
97 | 2.45M | assert(g_alloc_count > 0); |
98 | 2.45M | assert(g_alloc_bytes >= size); |
99 | 2.45M | ((size_t*)buf)[1] = 0; |
100 | 2.45M | memset(buf + PREFIX_SIZE + size, 0, sizeof(check2)); |
101 | 2.45M | g_alloc_count--; |
102 | 2.45M | g_alloc_bytes -= size; |
103 | 2.45M | free(buf); |
104 | 2.45M | } |
105 | 9.29M | } |
106 | | |
107 | | /* Reallocate block and check / write guard values */ |
108 | | void* realloc_with_check(void *ptr, size_t size) |
109 | 56.2M | { |
110 | 56.2M | if (!ptr && size) |
111 | 2.38M | { |
112 | | /* Allocate new block and write guard values */ |
113 | 2.38M | return malloc_with_check(size); |
114 | 2.38M | } |
115 | 53.8M | else if (ptr && size) |
116 | 53.8M | { |
117 | | /* Change block size */ |
118 | 53.8M | char *buf = (char*)ptr - PREFIX_SIZE; |
119 | 53.8M | size_t check2 = 0; |
120 | 53.8M | size_t oldsize = ((size_t*)buf)[0]; |
121 | 53.8M | assert(((size_t*)buf)[1] == CHECK1); |
122 | 53.8M | memcpy(&check2, buf + PREFIX_SIZE + oldsize, sizeof(check2)); |
123 | 53.8M | assert(check2 == CHECK2); |
124 | 53.8M | assert(g_alloc_count > 0); |
125 | 53.8M | assert(g_alloc_bytes >= oldsize); |
126 | | |
127 | 53.8M | if (size <= g_max_alloc_bytes - (g_alloc_bytes - oldsize)) |
128 | 53.8M | { |
129 | 53.8M | size_t new_rounded = round_blocksize(size + GUARD_SIZE); |
130 | 53.8M | size_t old_rounded = round_blocksize(oldsize + GUARD_SIZE); |
131 | | |
132 | 53.8M | if (new_rounded != old_rounded) |
133 | 610k | { |
134 | 610k | buf = realloc(buf, new_rounded); |
135 | 610k | } |
136 | 53.8M | } |
137 | 63 | else |
138 | 63 | { |
139 | 63 | buf = NULL; |
140 | 63 | } |
141 | | |
142 | 53.8M | if (!buf) |
143 | 63 | { |
144 | 63 | if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to %u failed\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)size); |
145 | 63 | return NULL; |
146 | 63 | } |
147 | | |
148 | 53.8M | ((size_t*)buf)[0] = size; |
149 | 53.8M | ((size_t*)buf)[1] = CHECK1; |
150 | 53.8M | memcpy(buf + PREFIX_SIZE + size, &check2, sizeof(check2)); |
151 | 53.8M | g_alloc_bytes -= oldsize; |
152 | 53.8M | g_alloc_bytes += size; |
153 | | |
154 | 53.8M | if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to 0x%04x/%u\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size); |
155 | 53.8M | return buf + PREFIX_SIZE; |
156 | 53.8M | } |
157 | 0 | else if (ptr && !size) |
158 | 0 | { |
159 | | /* Deallocate */ |
160 | 0 | free_with_check(ptr); |
161 | 0 | return NULL; |
162 | 0 | } |
163 | 0 | else |
164 | 0 | { |
165 | | /* No action */ |
166 | 0 | return NULL; |
167 | 0 | } |
168 | 56.2M | } |
169 | | |
170 | | /* Return total number of allocations not yet released */ |
171 | | size_t get_alloc_count() |
172 | 122k | { |
173 | 122k | return g_alloc_count; |
174 | 122k | } |
175 | | |
176 | | /* Return allocated size for a pointer returned from malloc(). */ |
177 | | size_t get_allocation_size(const void *mem) |
178 | 1.91M | { |
179 | 1.91M | char *buf = (char*)mem - PREFIX_SIZE; |
180 | 1.91M | return ((size_t*)buf)[0]; |
181 | 1.91M | } |
182 | | |
183 | | /* Get total number of allocated bytes */ |
184 | | size_t get_alloc_bytes() |
185 | 4.29k | { |
186 | 4.29k | return g_alloc_bytes; |
187 | 4.29k | } |
188 | | |
189 | | /* Set limit for allocation size */ |
190 | | void set_max_alloc_bytes(size_t max_bytes) |
191 | 8.58k | { |
192 | 8.58k | g_max_alloc_bytes = max_bytes; |
193 | 8.58k | } |
194 | | |
195 | | size_t get_max_alloc_bytes() |
196 | 4.29k | { |
197 | 4.29k | return g_max_alloc_bytes; |
198 | 4.29k | } |