| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 | /* The wrapper functions in this file work like regular malloc() and free(), * but store check values before and after the allocation. This helps to catch * any buffer overrun errors in the test cases. */#include "malloc_wrappers.h"#include <stdint.h>#include <stdio.h>#include <assert.h>#include <string.h>#define GUARD_SIZE (sizeof(size_t)*3)#define PREFIX_SIZE (sizeof(size_t)*2)#define CHECK1 ((size_t)0xDEADBEEF)#define CHECK2 ((size_t)0x600DCAFE)#ifndef MAX_ALLOC_BYTES#define MAX_ALLOC_BYTES 16*1024*1024#endif#ifndef DEBUG_MALLOC#define DEBUG_MALLOC 0#endifstatic size_t g_alloc_count = 0;static size_t g_alloc_bytes = 0;static size_t g_max_alloc_bytes = MAX_ALLOC_BYTES;#ifdef LLVMFUZZER/* LLVM libsanitizer has a realloc() implementation that always copies * the whole memory block, even if there would be space to expand it in * place. This gets pretty slow when fuzzing, so this wrapper limits the * realloc() calls by rounding allocation size upwards. Real world * realloc() implementations are hopefully smarter. */static size_t round_blocksize(size_t size){    if (size < 256)    {        return size;    }    else    {        return (size + 1023) / 1024 * 1024;    }}#elsestatic size_t round_blocksize(size_t size){    return size;}#endif/* Allocate memory and place check values before and after. */void* malloc_with_check(size_t size){    char *buf = NULL;    if (size <= g_max_alloc_bytes - g_alloc_bytes)    {        buf = malloc(round_blocksize(size + GUARD_SIZE));    }    if (buf)    {        ((size_t*)buf)[0] = size;        ((size_t*)buf)[1] = CHECK1;        ((size_t*)(buf + size))[2] = CHECK2;        g_alloc_count++;        g_alloc_bytes += size;        if (DEBUG_MALLOC) fprintf(stderr, "Alloc 0x%04x/%u\n", (unsigned)(uintptr_t)(buf + PREFIX_SIZE), (unsigned)size);        return buf + PREFIX_SIZE;    }    else    {        if (DEBUG_MALLOC) fprintf(stderr, "malloc(%u) failed\n", (unsigned)size);        return NULL;    }}/* Free memory allocated with malloc_with_check() and do the checks. */void free_with_check(void *mem){    if (mem)    {        char *buf = (char*)mem - PREFIX_SIZE;        size_t size = ((size_t*)buf)[0];        if (DEBUG_MALLOC) fprintf(stderr, "Release 0x%04x/%u\n", (unsigned)(uintptr_t)mem, (unsigned)size);        assert(((size_t*)buf)[1] == CHECK1);        assert(((size_t*)(buf + size))[2] == CHECK2);        assert(g_alloc_count > 0);        assert(g_alloc_bytes >= size);        ((size_t*)buf)[1] = 0;        ((size_t*)(buf + size))[2] = 0;        g_alloc_count--;        g_alloc_bytes -= size;        free(buf);    }}/* Reallocate block and check / write guard values */void* realloc_with_check(void *ptr, size_t size){    if (!ptr && size)    {        /* Allocate new block and write guard values */        return malloc_with_check(size);    }    else if (ptr && size)    {        /* Change block size */        char *buf = (char*)ptr - PREFIX_SIZE;        size_t oldsize = ((size_t*)buf)[0];        assert(((size_t*)buf)[1] == CHECK1);        assert(((size_t*)(buf + oldsize))[2] == CHECK2);        assert(g_alloc_count > 0);        assert(g_alloc_bytes >= oldsize);        if (size <= g_max_alloc_bytes - (g_alloc_bytes - oldsize))        {            size_t new_rounded = round_blocksize(size + GUARD_SIZE);            size_t old_rounded = round_blocksize(oldsize + GUARD_SIZE);            if (new_rounded != old_rounded)            {                buf = realloc(buf, new_rounded);            }        }        else        {            buf = NULL;        }        if (!buf)        {            if (DEBUG_MALLOC) fprintf(stderr, "Realloc 0x%04x/%u to %u failed\n", (unsigned)(uintptr_t)ptr, (unsigned)oldsize, (unsigned)size);            return NULL;        }        ((size_t*)buf)[0] = size;        ((size_t*)buf)[1] = CHECK1;        ((size_t*)(buf + size))[2] = CHECK2;        g_alloc_bytes -= oldsize;        g_alloc_bytes += size;        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);        return buf + PREFIX_SIZE;    }    else if (ptr && !size)    {        /* Deallocate */        free_with_check(ptr);        return NULL;    }    else    {        /* No action */        return NULL;    }}/* Return total number of allocations not yet released */size_t get_alloc_count(){    return g_alloc_count;}/* Return allocated size for a pointer returned from malloc(). */size_t get_allocation_size(const void *mem){    char *buf = (char*)mem - PREFIX_SIZE;    return ((size_t*)buf)[0];}/* Get total number of allocated bytes */size_t get_alloc_bytes(){    return g_alloc_bytes;}/* Set limit for allocation size */void set_max_alloc_bytes(size_t max_bytes){    g_max_alloc_bytes = max_bytes;}size_t get_max_alloc_bytes(){    return g_max_alloc_bytes;}
 |