Line data Source code
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 : 25 : static size_t g_alloc_count = 0; 26 : static size_t g_alloc_bytes = 0; 27 : static size_t g_max_alloc_bytes = MAX_ALLOC_BYTES; 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. */ 35 : static 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 47 40986703 : static size_t round_blocksize(size_t size) 48 : { 49 40986703 : return size; 50 : } 51 : #endif 52 : 53 : /* Allocate memory and place check values before and after. */ 54 1419922 : void* malloc_with_check(size_t size) 55 : { 56 1419922 : char *buf = NULL; 57 : 58 1419922 : if (size <= g_max_alloc_bytes - g_alloc_bytes) 59 : { 60 1419633 : buf = malloc(round_blocksize(size + GUARD_SIZE)); 61 : } 62 : 63 1419922 : if (buf) 64 : { 65 1419633 : ((size_t*)buf)[0] = size; 66 1419633 : ((size_t*)buf)[1] = CHECK1; 67 1419633 : ((size_t*)(buf + size))[2] = CHECK2; 68 1419633 : g_alloc_count++; 69 1419633 : g_alloc_bytes += size; 70 : if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size); 71 1419633 : return buf + PREFIX_SIZE; 72 : } 73 : else 74 : { 75 : if (DEBUG_MALLOC) fprintf(stderr, "malloc(%u) failed\n", (unsigned)size); 76 289 : return NULL; 77 : } 78 : } 79 : 80 : /* Free memory allocated with malloc_with_check() and do the checks. */ 81 3284877 : void free_with_check(void *mem) 82 : { 83 3284877 : if (mem) 84 : { 85 1419547 : char *buf = (char*)mem - PREFIX_SIZE; 86 1419547 : 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 1419547 : assert(((size_t*)buf)[1] == CHECK1); 89 1419547 : assert(((size_t*)(buf + size))[2] == CHECK2); 90 1419547 : assert(g_alloc_count > 0); 91 1419547 : assert(g_alloc_bytes >= size); 92 1419547 : ((size_t*)buf)[1] = 0; 93 1419547 : ((size_t*)(buf + size))[2] = 0; 94 1419547 : g_alloc_count--; 95 1419547 : g_alloc_bytes -= size; 96 1419547 : free(buf); 97 : } 98 3284877 : } 99 : 100 : /* Reallocate block and check / write guard values */ 101 21105104 : void* realloc_with_check(void *ptr, size_t size) 102 : { 103 21105104 : if (!ptr && size) 104 : { 105 : /* Allocate new block and write guard values */ 106 1321348 : return malloc_with_check(size); 107 : } 108 19783756 : else if (ptr && size) 109 : { 110 : /* Change block size */ 111 19783756 : char *buf = (char*)ptr - PREFIX_SIZE; 112 19783756 : size_t oldsize = ((size_t*)buf)[0]; 113 19783756 : assert(((size_t*)buf)[1] == CHECK1); 114 19783756 : assert(((size_t*)(buf + oldsize))[2] == CHECK2); 115 19783756 : assert(g_alloc_count > 0); 116 19783756 : assert(g_alloc_bytes >= oldsize); 117 : 118 19783756 : if (size <= g_max_alloc_bytes - (g_alloc_bytes - oldsize)) 119 : { 120 19783535 : size_t new_rounded = round_blocksize(size + GUARD_SIZE); 121 19783535 : size_t old_rounded = round_blocksize(oldsize + GUARD_SIZE); 122 : 123 19783535 : if (new_rounded != old_rounded) 124 : { 125 19174360 : buf = realloc(buf, new_rounded); 126 : } 127 : } 128 : else 129 : { 130 221 : buf = NULL; 131 : } 132 : 133 19783756 : 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 221 : return NULL; 137 : } 138 : 139 19783535 : ((size_t*)buf)[0] = size; 140 19783535 : ((size_t*)buf)[1] = CHECK1; 141 19783535 : ((size_t*)(buf + size))[2] = CHECK2; 142 19783535 : g_alloc_bytes -= oldsize; 143 19783535 : g_alloc_bytes += size; 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 19783535 : return buf + PREFIX_SIZE; 147 : } 148 0 : else if (ptr && !size) 149 : { 150 : /* Deallocate */ 151 0 : free_with_check(ptr); 152 0 : return NULL; 153 : } 154 : else 155 : { 156 : /* No action */ 157 0 : return NULL; 158 : } 159 : } 160 : 161 : /* Return total number of allocations not yet released */ 162 112503 : size_t get_alloc_count() 163 : { 164 112503 : return g_alloc_count; 165 : } 166 : 167 : /* Return allocated size for a pointer returned from malloc(). */ 168 979474 : size_t get_allocation_size(const void *mem) 169 : { 170 979474 : char *buf = (char*)mem - PREFIX_SIZE; 171 979474 : return ((size_t*)buf)[0]; 172 : } 173 : 174 : /* Get total number of allocated bytes */ 175 4847 : size_t get_alloc_bytes() 176 : { 177 4847 : return g_alloc_bytes; 178 : } 179 : 180 : /* Set limit for allocation size */ 181 9695 : void set_max_alloc_bytes(size_t max_bytes) 182 : { 183 9695 : g_max_alloc_bytes = max_bytes; 184 9695 : } 185 : 186 4847 : size_t get_max_alloc_bytes() 187 : { 188 4847 : return g_max_alloc_bytes; 189 : }