2003-03-15 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 <stdlib.h>
27
28
29 /**
30  * @defgroup DBusMemory Memory Allocation
31  * @ingroup  DBus
32  * @brief dbus_malloc(), dbus_free(), etc.
33  *
34  * Functions and macros related to allocating and releasing
35  * blocks of memory.
36  *
37  * @{
38  */
39
40 /**
41  * @def dbus_new
42  *
43  * Safe macro for using dbus_malloc(). Accepts the type
44  * to allocate and the number of type instances to
45  * allocate as arguments, and returns a memory block
46  * cast to the desired type, instead of as a void*.
47  *
48  * @param type type name to allocate
49  * @param count number of instances in the allocated array
50  * @returns the new memory block or #NULL on failure
51  */
52
53 /**
54  * @def dbus_new0
55  *
56  * Safe macro for using dbus_malloc0(). Accepts the type
57  * to allocate and the number of type instances to
58  * allocate as arguments, and returns a memory block
59  * cast to the desired type, instead of as a void*.
60  * The allocated array is initialized to all-bits-zero.
61  *
62  * @param type type name to allocate
63  * @param count number of instances in the allocated array
64  * @returns the new memory block or #NULL on failure
65  */
66
67 /**
68  * @typedef DBusFreeFunction
69  *
70  * The type of a function which frees a block of memory.
71  *
72  * @param memory the memory to free
73  */
74
75 #ifdef DBUS_BUILD_TESTS
76 static dbus_bool_t inited = FALSE;
77 static int fail_counts = -1;
78 static size_t fail_size = 0;
79 static dbus_bool_t guards = FALSE;
80 /** value stored in guard padding for debugging buffer overrun */
81 #define GUARD_VALUE 0xdeadbeef
82 /** size of the information about the block stored in guard mode */
83 #define GUARD_INFO_SIZE 8
84 /** size of the GUARD_VALUE-filled padding after the header info  */
85 #define GUARD_START_PAD 16
86 /** size of the GUARD_VALUE-filled padding at the end of the block */
87 #define GUARD_END_PAD 16
88 /** size of stuff at start of block */
89 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
90 /** total extra size over the requested allocation for guard stuff */
91 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
92 #endif
93
94 #ifdef DBUS_BUILD_TESTS
95 static void
96 initialize_malloc_debug (void)
97 {
98   if (!inited)
99     {
100       if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
101         {
102           fail_counts = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
103           _dbus_set_fail_alloc_counter (fail_counts);
104         }
105       
106       if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
107         fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
108
109       if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
110         guards = TRUE;
111       
112       inited = TRUE;
113     }
114 }
115
116 /**
117  * Where the block came from.
118  */
119 typedef enum
120 {
121   SOURCE_UNKNOWN,
122   SOURCE_MALLOC,
123   SOURCE_REALLOC,
124   SOURCE_MALLOC_ZERO,
125   SOURCE_REALLOC_NULL
126 } BlockSource;
127
128 static const char*
129 source_string (BlockSource source)
130 {
131   switch (source)
132     {
133     case SOURCE_UNKNOWN:
134       return "unknown";
135     case SOURCE_MALLOC:
136       return "malloc";
137     case SOURCE_REALLOC:
138       return "realloc";
139     case SOURCE_MALLOC_ZERO:
140       return "malloc0";
141     case SOURCE_REALLOC_NULL:
142       return "realloc(NULL)";
143     }
144   _dbus_assert_not_reached ("Invalid malloc block source ID");
145   return "invalid!";
146 }
147
148 static void
149 check_guards (void *free_block)
150 {
151   if (free_block != NULL)
152     {
153       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
154       size_t requested_bytes = *(dbus_uint32_t*)block;
155       BlockSource source = *(dbus_uint32_t*)(block + 4);
156       unsigned int i;
157       dbus_bool_t failed;
158
159       failed = FALSE;
160
161 #if 0
162       _dbus_verbose ("Checking %d bytes request from source %s\n",
163                      requested_bytes, source_string (source));
164 #endif
165       
166       i = GUARD_INFO_SIZE;
167       while (i < GUARD_START_OFFSET)
168         {
169           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
170           if (value != GUARD_VALUE)
171             {
172               _dbus_warn ("Block of %u bytes from %s had start guard value 0x%x at %d expected 0x%x\n",
173                           requested_bytes, source_string (source),
174                           value, i, GUARD_VALUE);
175               failed = TRUE;
176             }
177           
178           i += 4;
179         }
180
181       i = GUARD_START_OFFSET + requested_bytes;
182       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
183         {
184           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
185           if (value != GUARD_VALUE)
186             {
187               _dbus_warn ("Block of %u bytes from %s had end guard value 0x%x at %d expected 0x%x\n",
188                           requested_bytes, source_string (source),
189                           value, i, GUARD_VALUE);
190               failed = TRUE;
191             }
192           
193           i += 4;
194         }
195
196       if (failed)
197         _dbus_assert_not_reached ("guard value corruption");
198     }
199 }
200
201 static void*
202 set_guards (void       *real_block,
203             size_t      requested_bytes,
204             BlockSource source)
205 {
206   unsigned char *block = real_block;
207   unsigned int i;
208   
209   if (block == NULL)
210     return NULL;
211
212   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
213   
214   *((dbus_uint32_t*)block) = requested_bytes;
215   *((dbus_uint32_t*)(block + 4)) = source;
216
217   i = GUARD_INFO_SIZE;
218   while (i < GUARD_START_OFFSET)
219     {
220       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
221       
222       i += 4;
223     }
224
225   i = GUARD_START_OFFSET + requested_bytes;
226   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
227     {
228       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
229       
230       i += 4;
231     }
232   
233   check_guards (block + GUARD_START_OFFSET);
234   
235   return block + GUARD_START_OFFSET;
236 }
237
238 #endif
239
240 /**
241  * Allocates the given number of bytes, as with standard
242  * malloc(). Guaranteed to return #NULL if bytes is zero
243  * on all platforms. Returns #NULL if the allocation fails.
244  * The memory must be released with dbus_free().
245  *
246  * @param bytes number of bytes to allocate
247  * @return allocated memory, or #NULL if the allocation fails.
248  */
249 void*
250 dbus_malloc (size_t bytes)
251 {
252 #ifdef DBUS_BUILD_TESTS
253   initialize_malloc_debug ();
254   
255   if (_dbus_decrement_fail_alloc_counter ())
256     {
257       if (fail_counts != -1)
258         _dbus_set_fail_alloc_counter (fail_counts);
259
260       _dbus_verbose (" FAILING malloc of %d bytes\n", bytes);
261       
262       return NULL;
263     }
264 #endif
265   
266   if (bytes == 0) /* some system mallocs handle this, some don't */
267     return NULL;
268 #if DBUS_BUILD_TESTS
269   else if (fail_size != 0 && bytes > fail_size)
270     return NULL;
271   else if (guards)
272     {
273       void *block;
274
275       block = malloc (bytes + GUARD_EXTRA_SIZE);
276       return set_guards (block, bytes, SOURCE_MALLOC);
277     }
278 #endif
279   else
280     return malloc (bytes);
281 }
282
283 /**
284  * Allocates the given number of bytes, as with standard malloc(), but
285  * all bytes are initialized to zero as with calloc(). Guaranteed to
286  * return #NULL if bytes is zero on all platforms. Returns #NULL if the
287  * allocation fails.  The memory must be released with dbus_free().
288  *
289  * @param bytes number of bytes to allocate
290  * @return allocated memory, or #NULL if the allocation fails.
291  */
292 void*
293 dbus_malloc0 (size_t bytes)
294 {
295 #ifdef DBUS_BUILD_TESTS
296   initialize_malloc_debug ();
297   
298   if (_dbus_decrement_fail_alloc_counter ())
299     {
300       if (fail_counts != -1)
301         _dbus_set_fail_alloc_counter (fail_counts);
302
303       _dbus_verbose (" FAILING malloc0 of %d bytes\n", bytes);
304       
305       return NULL;
306     }
307 #endif
308
309   if (bytes == 0)
310     return NULL;
311 #if DBUS_BUILD_TESTS
312   else if (fail_size != 0 && bytes > fail_size)
313     return NULL;
314   else if (guards)
315     {
316       void *block;
317
318       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
319       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
320     }
321 #endif
322   else
323     return calloc (bytes, 1);
324 }
325
326 /**
327  * Resizes a block of memory previously allocated by dbus_malloc() or
328  * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes
329  * is zero on all platforms. Returns #NULL if the resize fails.
330  * If the resize fails, the memory is not freed.
331  *
332  * @param memory block to be resized
333  * @param bytes new size of the memory block
334  * @return allocated memory, or #NULL if the resize fails.
335  */
336 void*
337 dbus_realloc (void  *memory,
338               size_t bytes)
339 {
340 #ifdef DBUS_BUILD_TESTS
341   initialize_malloc_debug ();
342   
343   if (_dbus_decrement_fail_alloc_counter ())
344     {
345       if (fail_counts != -1)
346         _dbus_set_fail_alloc_counter (fail_counts);
347
348       _dbus_verbose (" FAILING realloc of %d bytes\n", bytes);
349       
350       return NULL;
351     }
352 #endif
353   
354   if (bytes == 0) /* guarantee this is safe */
355     {
356       dbus_free (memory);
357       return NULL;
358     }
359 #if DBUS_BUILD_TESTS
360   else if (fail_size != 0 && bytes > fail_size)
361     return NULL;
362   else if (guards)
363     {
364       if (memory)
365         {
366           void *block;
367           
368           check_guards (memory);
369           
370           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
371                            bytes + GUARD_EXTRA_SIZE);
372           
373           /* old guards shouldn't have moved */
374           check_guards (((unsigned char*)block) + GUARD_START_OFFSET);
375           
376           return set_guards (block, bytes, SOURCE_REALLOC);
377         }
378       else
379         {
380           void *block;
381           
382           block = malloc (bytes + GUARD_EXTRA_SIZE);
383           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
384         }
385     }
386 #endif
387   else
388     {
389       return realloc (memory, bytes);
390     }
391 }
392
393 /**
394  * Frees a block of memory previously allocated by dbus_malloc() or
395  * dbus_malloc0(). If passed #NULL, does nothing.
396  * 
397  * @param memory block to be freed
398  */
399 void
400 dbus_free (void  *memory)
401 {
402 #ifdef DBUS_BUILD_TESTS
403   if (guards)
404     {
405       check_guards (memory);
406       if (memory)
407         free (((unsigned char*)memory) - GUARD_START_OFFSET);
408       return;
409     }
410 #endif
411     
412   if (memory) /* we guarantee it's safe to free (NULL) */
413     free (memory);
414 }
415
416 /**
417  * Frees a #NULL-terminated array of strings.
418  * If passed #NULL, does nothing.
419  *
420  * @param str_array the array to be freed
421  */
422 void
423 dbus_free_string_array (char **str_array)
424 {
425   if (str_array)
426     {
427       int i;
428
429       i = 0;
430       while (str_array[i])
431         {
432           dbus_free (str_array[i]);
433           i++;
434         }
435
436       dbus_free (str_array);
437     }
438 }
439
440 /** @} */