MOS Source Code
Loading...
Searching...
No Matches
malloc_wrappers.c
Go to the documentation of this file.
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
6#include "malloc_wrappers.h"
7#include <stdint.h>
8#include <stdio.h>
9#include <assert.h>
10#include <string.h>
11
12#define GUARD_SIZE (sizeof(size_t)*3)
13#define PREFIX_SIZE (sizeof(size_t)*2)
14#define CHECK1 ((size_t)0xDEADBEEF)
15#define CHECK2 ((size_t)0x600DCAFE)
16
17#ifndef MAX_ALLOC_BYTES
18#define MAX_ALLOC_BYTES 16*1024*1024
19#endif
20
21#ifndef DEBUG_MALLOC
22#define DEBUG_MALLOC 0
23#endif
24
25static size_t g_alloc_count = 0;
26static size_t g_alloc_bytes = 0;
28
29#ifdef LLVMFUZZER
30/* LLVM libsanitizer has a realloc() implementation that always copies
31 * the whole memory block, even if there would be space to expand it in
32 * place. This gets pretty slow when fuzzing, so this wrapper limits the
33 * realloc() calls by rounding allocation size upwards. Real world
34 * realloc() implementations are hopefully smarter. */
35static size_t round_blocksize(size_t size)
36{
37 if (size < 256)
38 {
39 return size;
40 }
41 else
42 {
43 return (size + 1023) / 1024 * 1024;
44 }
45}
46#else
47static size_t round_blocksize(size_t size)
48{
49 return size;
50}
51#endif
52
53/* Allocate memory and place check values before and after. */
55{
56 char *buf = NULL;
57
59 {
60 buf = malloc(round_blocksize(size + GUARD_SIZE));
61 }
62
63 if (buf)
64 {
65 ((size_t*)buf)[0] = size;
66 ((size_t*)buf)[1] = CHECK1;
67 ((size_t*)(buf + size))[2] = CHECK2;
70 if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size);
71 return buf + PREFIX_SIZE;
72 }
73 else
74 {
75 if (DEBUG_MALLOC) fprintf(stderr, "malloc(%u) failed\n", (unsigned)size);
76 return NULL;
77 }
78}
79
80/* Free memory allocated with malloc_with_check() and do the checks. */
81void free_with_check(void *mem)
82{
83 if (mem)
84 {
85 char *buf = (char*)mem - PREFIX_SIZE;
86 size_t size = ((size_t*)buf)[0];
87 if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size);
88 assert(((size_t*)buf)[1] == CHECK1);
89 assert(((size_t*)(buf + size))[2] == CHECK2);
90 assert(g_alloc_count > 0);
91 assert(g_alloc_bytes >= size);
92 ((size_t*)buf)[1] = 0;
93 ((size_t*)(buf + size))[2] = 0;
96 free(buf);
97 }
98}
99
100/* Reallocate block and check / write guard values */
101void* realloc_with_check(void *ptr, size_t size)
102{
103 if (!ptr && size)
104 {
105 /* Allocate new block and write guard values */
106 return malloc_with_check(size);
107 }
108 else if (ptr && size)
109 {
110 /* Change block size */
111 char *buf = (char*)ptr - PREFIX_SIZE;
112 size_t oldsize = ((size_t*)buf)[0];
113 assert(((size_t*)buf)[1] == CHECK1);
114 assert(((size_t*)(buf + oldsize))[2] == CHECK2);
115 assert(g_alloc_count > 0);
116 assert(g_alloc_bytes >= oldsize);
117
118 if (size <= g_max_alloc_bytes - (g_alloc_bytes - oldsize))
119 {
120 size_t new_rounded = round_blocksize(size + GUARD_SIZE);
121 size_t old_rounded = round_blocksize(oldsize + GUARD_SIZE);
122
123 if (new_rounded != old_rounded)
124 {
125 buf = realloc(buf, new_rounded);
126 }
127 }
128 else
129 {
130 buf = NULL;
131 }
132
133 if (!buf)
134 {
135 if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to %u failed\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)size);
136 return NULL;
137 }
138
139 ((size_t*)buf)[0] = size;
140 ((size_t*)buf)[1] = CHECK1;
141 ((size_t*)(buf + size))[2] = CHECK2;
142 g_alloc_bytes -= oldsize;
144
145 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);
146 return buf + PREFIX_SIZE;
147 }
148 else if (ptr && !size)
149 {
150 /* Deallocate */
151 free_with_check(ptr);
152 return NULL;
153 }
154 else
155 {
156 /* No action */
157 return NULL;
158 }
159}
160
161/* Return total number of allocations not yet released */
163{
164 return g_alloc_count;
165}
166
167/* Return allocated size for a pointer returned from malloc(). */
168size_t get_allocation_size(const void *mem)
169{
170 char *buf = (char*)mem - PREFIX_SIZE;
171 return ((size_t*)buf)[0];
172}
173
174/* Get total number of allocated bytes */
176{
177 return g_alloc_bytes;
178}
179
180/* Set limit for allocation size */
181void set_max_alloc_bytes(size_t max_bytes)
182{
183 g_max_alloc_bytes = max_bytes;
184}
185
187{
188 return g_max_alloc_bytes;
189}
#define stderr
Definition mos_stdio.h:32
void * malloc_with_check(size_t size)
#define GUARD_SIZE
size_t get_allocation_size(const void *mem)
static size_t g_max_alloc_bytes
size_t get_alloc_count()
void free_with_check(void *mem)
size_t get_max_alloc_bytes()
#define MAX_ALLOC_BYTES
void * realloc_with_check(void *ptr, size_t size)
static size_t g_alloc_bytes
#define DEBUG_MALLOC
#define PREFIX_SIZE
static size_t g_alloc_count
static size_t round_blocksize(size_t size)
size_t get_alloc_bytes()
void set_max_alloc_bytes(size_t max_bytes)
#define CHECK2
#define CHECK1
#define NULL
Definition pb_syshdr.h:46
size_t size
Definition slab.c:30