/ libxml2 / testOOMlib.c
testOOMlib.c
  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@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  }