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