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