Home | History | Annotate | Download | only in libxml2
      1 /*
      2  * testOOM.c: Test out-of-memory handling
      3  *
      4  * See Copyright for the status of this software.
      5  *
      6  * Copyright 2003 Red Hat, Inc.
      7  * Written by: hp (at) redhat.com
      8  */
      9 
     10 #include "testOOMlib.h"
     11 
     12 #ifdef HAVE_STDLIB_H
     13 #include <stdlib.h>
     14 #endif
     15 
     16 #include <string.h>
     17 
     18 #define _TEST_INT_MAX 2147483647
     19 #ifndef TRUE
     20 #define TRUE (1)
     21 #endif
     22 #ifndef FALSE
     23 #define FALSE (0)
     24 #endif
     25 #ifndef NULL
     26 #define NULL ((void*)0)
     27 #endif
     28 
     29 #include <libxml/xmlmemory.h>
     30 
     31 static int fail_alloc_counter = _TEST_INT_MAX;
     32 static int n_failures_per_failure = 1;
     33 static int n_failures_this_failure = 0;
     34 static int n_blocks_outstanding = 0;
     35 
     36 /**
     37  * set_fail_alloc_counter:
     38  * @until_next_fail: number of successful allocs before one fails
     39  *
     40  * Sets the number of allocations until we simulate a failed
     41  * allocation. If set to 0, the next allocation to run
     42  * fails; if set to 1, one succeeds then the next fails; etc.
     43  * Set to _TEST_INT_MAX to not fail anything.
     44  */
     45 static void
     46 set_fail_alloc_counter (int until_next_fail)
     47 {
     48   fail_alloc_counter = until_next_fail;
     49 }
     50 
     51 /**
     52  * get_fail_alloc_counter:
     53  *
     54  * Returns the number of successful allocs until we'll simulate
     55  * a failed alloc.
     56  */
     57 static int
     58 get_fail_alloc_counter (void)
     59 {
     60   return fail_alloc_counter;
     61 }
     62 
     63 /**
     64  * set_fail_alloc_failures:
     65  * @failures_per_failure: number to fail
     66  *
     67  * Sets how many mallocs to fail when the fail alloc counter reaches
     68  * 0.
     69  *
     70  */
     71 static void
     72 set_fail_alloc_failures (int failures_per_failure)
     73 {
     74   n_failures_per_failure = failures_per_failure;
     75 }
     76 
     77 /**
     78  * decrement_fail_alloc_counter:
     79  *
     80  * Called when about to alloc some memory; if
     81  * it returns #TRUE, then the allocation should
     82  * fail. If it returns #FALSE, then the allocation
     83  * should not fail.
     84  *
     85  * returns #TRUE if this alloc should fail
     86  */
     87 static int
     88 decrement_fail_alloc_counter (void)
     89 {
     90   if (fail_alloc_counter <= 0)
     91     {
     92       n_failures_this_failure += 1;
     93       if (n_failures_this_failure >= n_failures_per_failure)
     94         {
     95           fail_alloc_counter = _TEST_INT_MAX;
     96 
     97           n_failures_this_failure = 0;
     98         }
     99 
    100       return TRUE;
    101     }
    102   else
    103     {
    104       fail_alloc_counter -= 1;
    105       return FALSE;
    106     }
    107 }
    108 
    109 /**
    110  * test_get_malloc_blocks_outstanding:
    111  *
    112  * Get the number of outstanding malloc()'d blocks.
    113  *
    114  * Returns number of blocks
    115  */
    116 int
    117 test_get_malloc_blocks_outstanding (void)
    118 {
    119   return n_blocks_outstanding;
    120 }
    121 
    122 void*
    123 test_malloc (size_t bytes)
    124 {
    125   if (decrement_fail_alloc_counter ())
    126     {
    127       /* FAIL the malloc */
    128       return NULL;
    129     }
    130 
    131   if (bytes == 0) /* some system mallocs handle this, some don't */
    132     return NULL;
    133   else
    134     {
    135       void *mem;
    136       mem = xmlMemMalloc (bytes);
    137 
    138       if (mem)
    139         n_blocks_outstanding += 1;
    140 
    141       return mem;
    142     }
    143 }
    144 
    145 void*
    146 test_realloc (void  *memory,
    147               size_t bytes)
    148 {
    149   if (decrement_fail_alloc_counter ())
    150     {
    151       /* FAIL */
    152       return NULL;
    153     }
    154 
    155   if (bytes == 0) /* guarantee this is safe */
    156     {
    157       test_free (memory);
    158       return NULL;
    159     }
    160   else
    161     {
    162       void *mem;
    163       mem = xmlMemRealloc (memory, bytes);
    164 
    165       if (memory == NULL && mem != NULL)
    166         n_blocks_outstanding += 1;
    167 
    168       return mem;
    169     }
    170 }
    171 
    172 void
    173 test_free (void  *memory)
    174 {
    175   if (memory) /* we guarantee it's safe to free (NULL) */
    176     {
    177       n_blocks_outstanding -= 1;
    178 
    179       xmlMemFree (memory);
    180     }
    181 }
    182 
    183 char*
    184 test_strdup (const char *str)
    185 {
    186   int len;
    187   char *copy;
    188 
    189   if (str == NULL)
    190     return NULL;
    191 
    192   len = strlen (str);
    193 
    194   copy = test_malloc (len + 1);
    195   if (copy == NULL)
    196     return NULL;
    197 
    198   memcpy (copy, str, len + 1);
    199 
    200   return copy;
    201 }
    202 
    203 static int
    204 run_failing_each_malloc (int                n_mallocs,
    205                          TestMemoryFunction func,
    206                          void              *data)
    207 {
    208   n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
    209 
    210   while (n_mallocs >= 0)
    211     {
    212       set_fail_alloc_counter (n_mallocs);
    213 
    214       if (!(* func) (data))
    215         return FALSE;
    216 
    217       n_mallocs -= 1;
    218     }
    219 
    220   set_fail_alloc_counter (_TEST_INT_MAX);
    221 
    222   return TRUE;
    223 }
    224 
    225 /**
    226  * test_oom_handling:
    227  * @func: function to call
    228  * @data: data to pass to function
    229  *
    230  * Tests how well the given function responds to out-of-memory
    231  * situations. Calls the function repeatedly, failing a different
    232  * call to malloc() each time. If the function ever returns #FALSE,
    233  * the test fails. The function should return #TRUE whenever something
    234  * valid (such as returning an error, or succeeding) occurs, and #FALSE
    235  * if it gets confused in some way.
    236  *
    237  * Returns #TRUE if the function never returns FALSE
    238  */
    239 int
    240 test_oom_handling (TestMemoryFunction  func,
    241                    void               *data)
    242 {
    243   int approx_mallocs;
    244 
    245   /* Run once to see about how many mallocs are involved */
    246 
    247   set_fail_alloc_counter (_TEST_INT_MAX);
    248 
    249   if (!(* func) (data))
    250     return FALSE;
    251 
    252   approx_mallocs = _TEST_INT_MAX - get_fail_alloc_counter ();
    253 
    254   set_fail_alloc_failures (1);
    255   if (!run_failing_each_malloc (approx_mallocs, func, data))
    256     return FALSE;
    257 
    258   set_fail_alloc_failures (2);
    259   if (!run_failing_each_malloc (approx_mallocs, func, data))
    260     return FALSE;
    261 
    262   set_fail_alloc_failures (3);
    263   if (!run_failing_each_malloc (approx_mallocs, func, data))
    264     return FALSE;
    265 
    266   set_fail_alloc_counter (_TEST_INT_MAX);
    267 
    268   return TRUE;
    269 }
    270