2003-03-16 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-memory.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-memory.c  D-BUS memory handling
3  *
4  * Copyright (C) 2002, 2003  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-memory.h"
25 #include "dbus-internals.h"
26 #include "dbus-sysdeps.h"
27 #include <stdlib.h>
28
29
30 /**
31  * @defgroup DBusMemory Memory Allocation
32  * @ingroup  DBus
33  * @brief dbus_malloc(), dbus_free(), etc.
34  *
35  * Functions and macros related to allocating and releasing
36  * blocks of memory.
37  *
38  * @{
39  */
40
41 /**
42  * @def dbus_new
43  *
44  * Safe macro for using dbus_malloc(). Accepts the type
45  * to allocate and the number of type instances to
46  * allocate as arguments, and returns a memory block
47  * cast to the desired type, instead of as a void*.
48  *
49  * @param type type name to allocate
50  * @param count number of instances in the allocated array
51  * @returns the new memory block or #NULL on failure
52  */
53
54 /**
55  * @def dbus_new0
56  *
57  * Safe macro for using dbus_malloc0(). Accepts the type
58  * to allocate and the number of type instances to
59  * allocate as arguments, and returns a memory block
60  * cast to the desired type, instead of as a void*.
61  * The allocated array is initialized to all-bits-zero.
62  *
63  * @param type type name to allocate
64  * @param count number of instances in the allocated array
65  * @returns the new memory block or #NULL on failure
66  */
67
68 /**
69  * @typedef DBusFreeFunction
70  *
71  * The type of a function which frees a block of memory.
72  *
73  * @param memory the memory to free
74  */
75
76 #ifdef DBUS_BUILD_TESTS
77 static dbus_bool_t debug_initialized = FALSE;
78 static int fail_counts = -1;
79 static size_t fail_size = 0;
80 static int fail_alloc_counter = _DBUS_INT_MAX;
81 static dbus_bool_t guards = FALSE;
82 static dbus_bool_t disable_mem_pools = FALSE;
83 static dbus_bool_t backtrace_on_fail_alloc = FALSE;
84
85 /** value stored in guard padding for debugging buffer overrun */
86 #define GUARD_VALUE 0xdeadbeef
87 /** size of the information about the block stored in guard mode */
88 #define GUARD_INFO_SIZE 8
89 /** size of the GUARD_VALUE-filled padding after the header info  */
90 #define GUARD_START_PAD 16
91 /** size of the GUARD_VALUE-filled padding at the end of the block */
92 #define GUARD_END_PAD 16
93 /** size of stuff at start of block */
94 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
95 /** total extra size over the requested allocation for guard stuff */
96 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
97
98 static void
99 _dbus_initialize_malloc_debug (void)
100 {
101   if (!debug_initialized)
102     {
103       debug_initialized = TRUE;
104       
105       if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
106         {
107           fail_counts = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
108           fail_alloc_counter = fail_counts;
109           _dbus_verbose ("Will fail malloc every %d times\n", fail_counts);
110         }
111       
112       if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
113         {
114           fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
115           _dbus_verbose ("Will fail mallocs over %d bytes\n",
116                          fail_size);
117         }
118
119       if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
120         {
121           guards = TRUE;
122           _dbus_verbose ("Will use malloc guards\n");
123         }
124
125       if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
126         {
127           disable_mem_pools = TRUE;
128           _dbus_verbose ("Will disable memory pools\n");
129         }
130
131       if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
132         {
133           backtrace_on_fail_alloc = TRUE;
134           _dbus_verbose ("Will backtrace on failing a malloc\n");
135         }
136     }
137 }
138
139 /**
140  * Whether to turn off mem pools, useful for leak checking.
141  *
142  * @returns #TRUE if mempools should not be used.
143  */
144 dbus_bool_t
145 _dbus_disable_mem_pools (void)
146 {
147   _dbus_initialize_malloc_debug ();
148   return disable_mem_pools;
149 }
150
151 /**
152  * Sets the number of allocations until we simulate a failed
153  * allocation. If set to 0, the next allocation to run
154  * fails; if set to 1, one succeeds then the next fails; etc.
155  * Set to _DBUS_INT_MAX to not fail anything. 
156  *
157  * @param until_next_fail number of successful allocs before one fails
158  */
159 void
160 _dbus_set_fail_alloc_counter (int until_next_fail)
161 {
162   _dbus_initialize_malloc_debug ();
163
164   fail_alloc_counter = until_next_fail;
165
166 #if 0
167   _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
168 #endif
169 }
170
171 /**
172  * Gets the number of successful allocs until we'll simulate
173  * a failed alloc.
174  *
175  * @returns current counter value
176  */
177 int
178 _dbus_get_fail_alloc_counter (void)
179 {
180   _dbus_initialize_malloc_debug ();
181
182   return fail_alloc_counter;
183 }
184
185 /**
186  * Called when about to alloc some memory; if
187  * it returns #TRUE, then the allocation should
188  * fail. If it returns #FALSE, then the allocation
189  * should not fail.
190  *
191  * @returns #TRUE if this alloc should fail
192  */
193 dbus_bool_t
194 _dbus_decrement_fail_alloc_counter (void)
195 {
196   _dbus_initialize_malloc_debug ();
197   
198   if (fail_alloc_counter <= 0)
199     {
200       if (fail_counts >= 0)
201         fail_alloc_counter = fail_counts;
202       else
203         fail_alloc_counter = _DBUS_INT_MAX;
204
205       _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
206       if (backtrace_on_fail_alloc)
207         _dbus_print_backtrace ();
208       
209       return TRUE;
210     }
211   else
212     {
213       fail_alloc_counter -= 1;
214       return FALSE;
215     }
216 }
217
218 /**
219  * Where the block came from.
220  */
221 typedef enum
222 {
223   SOURCE_UNKNOWN,
224   SOURCE_MALLOC,
225   SOURCE_REALLOC,
226   SOURCE_MALLOC_ZERO,
227   SOURCE_REALLOC_NULL
228 } BlockSource;
229
230 static const char*
231 source_string (BlockSource source)
232 {
233   switch (source)
234     {
235     case SOURCE_UNKNOWN:
236       return "unknown";
237     case SOURCE_MALLOC:
238       return "malloc";
239     case SOURCE_REALLOC:
240       return "realloc";
241     case SOURCE_MALLOC_ZERO:
242       return "malloc0";
243     case SOURCE_REALLOC_NULL:
244       return "realloc(NULL)";
245     }
246   _dbus_assert_not_reached ("Invalid malloc block source ID");
247   return "invalid!";
248 }
249
250 static void
251 check_guards (void *free_block)
252 {
253   if (free_block != NULL)
254     {
255       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
256       size_t requested_bytes = *(dbus_uint32_t*)block;
257       BlockSource source = *(dbus_uint32_t*)(block + 4);
258       unsigned int i;
259       dbus_bool_t failed;
260
261       failed = FALSE;
262
263 #if 0
264       _dbus_verbose ("Checking %d bytes request from source %s\n",
265                      requested_bytes, source_string (source));
266 #endif
267       
268       i = GUARD_INFO_SIZE;
269       while (i < GUARD_START_OFFSET)
270         {
271           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
272           if (value != GUARD_VALUE)
273             {
274               _dbus_warn ("Block of %u bytes from %s had start guard value 0x%x at %d expected 0x%x\n",
275                           requested_bytes, source_string (source),
276                           value, i, GUARD_VALUE);
277               failed = TRUE;
278             }
279           
280           i += 4;
281         }
282
283       i = GUARD_START_OFFSET + requested_bytes;
284       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
285         {
286           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
287           if (value != GUARD_VALUE)
288             {
289               _dbus_warn ("Block of %u bytes from %s had end guard value 0x%x at %d expected 0x%x\n",
290                           requested_bytes, source_string (source),
291                           value, i, GUARD_VALUE);
292               failed = TRUE;
293             }
294           
295           i += 4;
296         }
297
298       if (failed)
299         _dbus_assert_not_reached ("guard value corruption");
300     }
301 }
302
303 static void*
304 set_guards (void       *real_block,
305             size_t      requested_bytes,
306             BlockSource source)
307 {
308   unsigned char *block = real_block;
309   unsigned int i;
310   
311   if (block == NULL)
312     return NULL;
313
314   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
315   
316   *((dbus_uint32_t*)block) = requested_bytes;
317   *((dbus_uint32_t*)(block + 4)) = source;
318
319   i = GUARD_INFO_SIZE;
320   while (i < GUARD_START_OFFSET)
321     {
322       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
323       
324       i += 4;
325     }
326
327   i = GUARD_START_OFFSET + requested_bytes;
328   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
329     {
330       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
331       
332       i += 4;
333     }
334   
335   check_guards (block + GUARD_START_OFFSET);
336   
337   return block + GUARD_START_OFFSET;
338 }
339
340 #endif
341
342 /**
343  * Allocates the given number of bytes, as with standard
344  * malloc(). Guaranteed to return #NULL if bytes is zero
345  * on all platforms. Returns #NULL if the allocation fails.
346  * The memory must be released with dbus_free().
347  *
348  * @param bytes number of bytes to allocate
349  * @return allocated memory, or #NULL if the allocation fails.
350  */
351 void*
352 dbus_malloc (size_t bytes)
353 {
354 #ifdef DBUS_BUILD_TESTS
355   _dbus_initialize_malloc_debug ();
356   
357   if (_dbus_decrement_fail_alloc_counter ())
358     {
359       _dbus_verbose (" FAILING malloc of %d bytes\n", bytes);
360       
361       return NULL;
362     }
363 #endif
364   
365   if (bytes == 0) /* some system mallocs handle this, some don't */
366     return NULL;
367 #if DBUS_BUILD_TESTS
368   else if (fail_size != 0 && bytes > fail_size)
369     return NULL;
370   else if (guards)
371     {
372       void *block;
373
374       block = malloc (bytes + GUARD_EXTRA_SIZE);
375       return set_guards (block, bytes, SOURCE_MALLOC);
376     }
377 #endif
378   else
379     return malloc (bytes);
380 }
381
382 /**
383  * Allocates the given number of bytes, as with standard malloc(), but
384  * all bytes are initialized to zero as with calloc(). Guaranteed to
385  * return #NULL if bytes is zero on all platforms. Returns #NULL if the
386  * allocation fails.  The memory must be released with dbus_free().
387  *
388  * @param bytes number of bytes to allocate
389  * @return allocated memory, or #NULL if the allocation fails.
390  */
391 void*
392 dbus_malloc0 (size_t bytes)
393 {
394 #ifdef DBUS_BUILD_TESTS
395   _dbus_initialize_malloc_debug ();
396   
397   if (_dbus_decrement_fail_alloc_counter ())
398     {
399       _dbus_verbose (" FAILING malloc0 of %d bytes\n", bytes);
400       
401       return NULL;
402     }
403 #endif
404
405   if (bytes == 0)
406     return NULL;
407 #if DBUS_BUILD_TESTS
408   else if (fail_size != 0 && bytes > fail_size)
409     return NULL;
410   else if (guards)
411     {
412       void *block;
413
414       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
415       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
416     }
417 #endif
418   else
419     return calloc (bytes, 1);
420 }
421
422 /**
423  * Resizes a block of memory previously allocated by dbus_malloc() or
424  * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes
425  * is zero on all platforms. Returns #NULL if the resize fails.
426  * If the resize fails, the memory is not freed.
427  *
428  * @param memory block to be resized
429  * @param bytes new size of the memory block
430  * @return allocated memory, or #NULL if the resize fails.
431  */
432 void*
433 dbus_realloc (void  *memory,
434               size_t bytes)
435 {
436 #ifdef DBUS_BUILD_TESTS
437   _dbus_initialize_malloc_debug ();
438   
439   if (_dbus_decrement_fail_alloc_counter ())
440     {
441       _dbus_verbose (" FAILING realloc of %d bytes\n", bytes);
442       
443       return NULL;
444     }
445 #endif
446   
447   if (bytes == 0) /* guarantee this is safe */
448     {
449       dbus_free (memory);
450       return NULL;
451     }
452 #if DBUS_BUILD_TESTS
453   else if (fail_size != 0 && bytes > fail_size)
454     return NULL;
455   else if (guards)
456     {
457       if (memory)
458         {
459           void *block;
460           
461           check_guards (memory);
462           
463           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
464                            bytes + GUARD_EXTRA_SIZE);
465           
466           /* old guards shouldn't have moved */
467           check_guards (((unsigned char*)block) + GUARD_START_OFFSET);
468           
469           return set_guards (block, bytes, SOURCE_REALLOC);
470         }
471       else
472         {
473           void *block;
474           
475           block = malloc (bytes + GUARD_EXTRA_SIZE);
476           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
477         }
478     }
479 #endif
480   else
481     {
482       return realloc (memory, bytes);
483     }
484 }
485
486 /**
487  * Frees a block of memory previously allocated by dbus_malloc() or
488  * dbus_malloc0(). If passed #NULL, does nothing.
489  * 
490  * @param memory block to be freed
491  */
492 void
493 dbus_free (void  *memory)
494 {
495 #ifdef DBUS_BUILD_TESTS
496   if (guards)
497     {
498       check_guards (memory);
499       if (memory)
500         free (((unsigned char*)memory) - GUARD_START_OFFSET);
501       return;
502     }
503 #endif
504     
505   if (memory) /* we guarantee it's safe to free (NULL) */
506     free (memory);
507 }
508
509 /**
510  * Frees a #NULL-terminated array of strings.
511  * If passed #NULL, does nothing.
512  *
513  * @param str_array the array to be freed
514  */
515 void
516 dbus_free_string_array (char **str_array)
517 {
518   if (str_array)
519     {
520       int i;
521
522       i = 0;
523       while (str_array[i])
524         {
525           dbus_free (str_array[i]);
526           i++;
527         }
528
529       dbus_free (str_array);
530     }
531 }
532
533 /** @} */