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
set_fail_alloc_counter(int until_next_fail)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
get_fail_alloc_counter(void)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
set_fail_alloc_failures(int failures_per_failure)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
decrement_fail_alloc_counter(void)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
test_get_malloc_blocks_outstanding(void)117 test_get_malloc_blocks_outstanding (void)
118 {
119 return n_blocks_outstanding;
120 }
121
122 void*
test_malloc(size_t bytes)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*
test_realloc(void * memory,size_t bytes)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
test_free(void * memory)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*
test_strdup(const char * str)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
run_failing_each_malloc(int n_mallocs,TestMemoryFunction func,void * data)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
test_oom_handling(TestMemoryFunction func,void * data)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