LCOV - code coverage report
Current view: top level - common - malloc_wrappers.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 64 68 94.1 %
Date: 2023-02-14 20:10:26 Functions: 9 9 100.0 %

          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             : }

Generated by: LCOV version 1.14