D-Bus 1.4.14
dbus-memory.c
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-memory.c  D-Bus memory handling
00003  *
00004  * Copyright (C) 2002, 2003  Red Hat Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-memory.h"
00026 #include "dbus-internals.h"
00027 #include "dbus-sysdeps.h"
00028 #include "dbus-list.h"
00029 #include <stdlib.h>
00030  /* end of public API docs */
00092 
00099 #ifdef DBUS_BUILD_TESTS
00100 static dbus_bool_t debug_initialized = FALSE;
00101 static int fail_nth = -1;
00102 static size_t fail_size = 0;
00103 static int fail_alloc_counter = _DBUS_INT_MAX;
00104 static int n_failures_per_failure = 1;
00105 static int n_failures_this_failure = 0;
00106 static dbus_bool_t guards = FALSE;
00107 static dbus_bool_t disable_mem_pools = FALSE;
00108 static dbus_bool_t backtrace_on_fail_alloc = FALSE;
00109 static DBusAtomic n_blocks_outstanding = {0};
00110 
00112 #define GUARD_VALUE 0xdeadbeef
00113 
00114 #define GUARD_INFO_SIZE 8
00115 
00116 #define GUARD_START_PAD 16
00117 
00118 #define GUARD_END_PAD 16
00119 
00120 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
00121 
00122 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
00123 
00124 static void
00125 _dbus_initialize_malloc_debug (void)
00126 {
00127   if (!debug_initialized)
00128     {
00129       debug_initialized = TRUE;
00130       
00131       if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
00132         {
00133           fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
00134           fail_alloc_counter = fail_nth;
00135           _dbus_verbose ("Will fail malloc every %d times\n", fail_nth);
00136         }
00137       
00138       if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
00139         {
00140           fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
00141           _dbus_verbose ("Will fail mallocs over %ld bytes\n",
00142                          (long) fail_size);
00143         }
00144 
00145       if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
00146         {
00147           guards = TRUE;
00148           _dbus_verbose ("Will use malloc guards\n");
00149         }
00150 
00151       if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
00152         {
00153           disable_mem_pools = TRUE;
00154           _dbus_verbose ("Will disable memory pools\n");
00155         }
00156 
00157       if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
00158         {
00159           backtrace_on_fail_alloc = TRUE;
00160           _dbus_verbose ("Will backtrace on failing a malloc\n");
00161         }
00162     }
00163 }
00164 
00170 dbus_bool_t
00171 _dbus_disable_mem_pools (void)
00172 {
00173   _dbus_initialize_malloc_debug ();
00174   return disable_mem_pools;
00175 }
00176 
00185 void
00186 _dbus_set_fail_alloc_counter (int until_next_fail)
00187 {
00188   _dbus_initialize_malloc_debug ();
00189 
00190   fail_alloc_counter = until_next_fail;
00191 
00192 #if 0
00193   _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
00194 #endif
00195 }
00196 
00203 int
00204 _dbus_get_fail_alloc_counter (void)
00205 {
00206   _dbus_initialize_malloc_debug ();
00207 
00208   return fail_alloc_counter;
00209 }
00210 
00217 void
00218 _dbus_set_fail_alloc_failures (int failures_per_failure)
00219 {
00220   n_failures_per_failure = failures_per_failure;
00221 }
00222 
00229 int
00230 _dbus_get_fail_alloc_failures (void)
00231 {
00232   return n_failures_per_failure;
00233 }
00234 
00235 #ifdef DBUS_BUILD_TESTS
00236 static dbus_bool_t called = 0;
00245 dbus_bool_t
00246 _dbus_decrement_fail_alloc_counter (void)
00247 {
00248   _dbus_initialize_malloc_debug ();
00249 #ifdef DBUS_WIN_FIXME
00250   {
00251     if (!called)
00252       {
00253         _dbus_verbose("TODO: memory allocation testing errors disabled for now\n");
00254         called = 1;
00255       }
00256     return FALSE;
00257   }
00258 #endif
00259 
00260   if (fail_alloc_counter <= 0)
00261     {
00262       if (backtrace_on_fail_alloc)
00263         _dbus_print_backtrace ();
00264 
00265       _dbus_verbose ("failure %d\n", n_failures_this_failure);
00266       
00267       n_failures_this_failure += 1;
00268       if (n_failures_this_failure >= n_failures_per_failure)
00269         {
00270           if (fail_nth >= 0)
00271             fail_alloc_counter = fail_nth;
00272           else
00273             fail_alloc_counter = _DBUS_INT_MAX;
00274 
00275           n_failures_this_failure = 0;
00276 
00277           _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
00278         }
00279       
00280       return TRUE;
00281     }
00282   else
00283     {
00284       fail_alloc_counter -= 1;
00285       return FALSE;
00286     }
00287 }
00288 #endif /* DBUS_BUILD_TESTS */
00289 
00295 int
00296 _dbus_get_malloc_blocks_outstanding (void)
00297 {
00298   return _dbus_atomic_get (&n_blocks_outstanding);
00299 }
00300 
00304 typedef enum
00305 {
00306   SOURCE_UNKNOWN,
00307   SOURCE_MALLOC,
00308   SOURCE_REALLOC,
00309   SOURCE_MALLOC_ZERO,
00310   SOURCE_REALLOC_NULL
00311 } BlockSource;
00312 
00313 static const char*
00314 source_string (BlockSource source)
00315 {
00316   switch (source)
00317     {
00318     case SOURCE_UNKNOWN:
00319       return "unknown";
00320     case SOURCE_MALLOC:
00321       return "malloc";
00322     case SOURCE_REALLOC:
00323       return "realloc";
00324     case SOURCE_MALLOC_ZERO:
00325       return "malloc0";
00326     case SOURCE_REALLOC_NULL:
00327       return "realloc(NULL)";
00328     }
00329   _dbus_assert_not_reached ("Invalid malloc block source ID");
00330   return "invalid!";
00331 }
00332 
00333 static void
00334 check_guards (void       *free_block,
00335               dbus_bool_t overwrite)
00336 {
00337   if (free_block != NULL)
00338     {
00339       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
00340       size_t requested_bytes = *(dbus_uint32_t*)block;
00341       BlockSource source = *(dbus_uint32_t*)(block + 4);
00342       unsigned int i;
00343       dbus_bool_t failed;
00344 
00345       failed = FALSE;
00346 
00347 #if 0
00348       _dbus_verbose ("Checking %d bytes request from source %s\n",
00349                      requested_bytes, source_string (source));
00350 #endif
00351       
00352       i = GUARD_INFO_SIZE;
00353       while (i < GUARD_START_OFFSET)
00354         {
00355           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00356           if (value != GUARD_VALUE)
00357             {
00358               _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n",
00359                           (long) requested_bytes, source_string (source),
00360                           value, i, GUARD_VALUE);
00361               failed = TRUE;
00362             }
00363           
00364           i += 4;
00365         }
00366 
00367       i = GUARD_START_OFFSET + requested_bytes;
00368       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00369         {
00370           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00371           if (value != GUARD_VALUE)
00372             {
00373               _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n",
00374                           (long) requested_bytes, source_string (source),
00375                           value, i, GUARD_VALUE);
00376               failed = TRUE;
00377             }
00378           
00379           i += 4;
00380         }
00381 
00382       /* set memory to anything but nul bytes */
00383       if (overwrite)
00384         memset (free_block, 'g', requested_bytes);
00385       
00386       if (failed)
00387         _dbus_assert_not_reached ("guard value corruption");
00388     }
00389 }
00390 
00391 static void*
00392 set_guards (void       *real_block,
00393             size_t      requested_bytes,
00394             BlockSource source)
00395 {
00396   unsigned char *block = real_block;
00397   unsigned int i;
00398   
00399   if (block == NULL)
00400     return NULL;
00401 
00402   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
00403   
00404   *((dbus_uint32_t*)block) = requested_bytes;
00405   *((dbus_uint32_t*)(block + 4)) = source;
00406 
00407   i = GUARD_INFO_SIZE;
00408   while (i < GUARD_START_OFFSET)
00409     {
00410       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00411       
00412       i += 4;
00413     }
00414 
00415   i = GUARD_START_OFFSET + requested_bytes;
00416   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00417     {
00418       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00419       
00420       i += 4;
00421     }
00422   
00423   check_guards (block + GUARD_START_OFFSET, FALSE);
00424   
00425   return block + GUARD_START_OFFSET;
00426 }
00427 
00428 #endif
00429  /* End of internals docs */
00431 
00432 
00451 void*
00452 dbus_malloc (size_t bytes)
00453 {
00454 #ifdef DBUS_BUILD_TESTS
00455   _dbus_initialize_malloc_debug ();
00456   
00457   if (_dbus_decrement_fail_alloc_counter ())
00458     {
00459       _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
00460       return NULL;
00461     }
00462 #endif
00463 
00464   if (bytes == 0) /* some system mallocs handle this, some don't */
00465     return NULL;
00466 #ifdef DBUS_BUILD_TESTS
00467   else if (fail_size != 0 && bytes > fail_size)
00468     return NULL;
00469   else if (guards)
00470     {
00471       void *block;
00472 
00473       block = malloc (bytes + GUARD_EXTRA_SIZE);
00474       if (block)
00475         _dbus_atomic_inc (&n_blocks_outstanding);
00476       
00477       return set_guards (block, bytes, SOURCE_MALLOC);
00478     }
00479 #endif
00480   else
00481     {
00482       void *mem;
00483       mem = malloc (bytes);
00484 #ifdef DBUS_BUILD_TESTS
00485       if (mem)
00486         _dbus_atomic_inc (&n_blocks_outstanding);
00487 #endif
00488       return mem;
00489     }
00490 }
00491 
00504 void*
00505 dbus_malloc0 (size_t bytes)
00506 {
00507 #ifdef DBUS_BUILD_TESTS
00508   _dbus_initialize_malloc_debug ();
00509   
00510   if (_dbus_decrement_fail_alloc_counter ())
00511     {
00512       _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
00513       
00514       return NULL;
00515     }
00516 #endif
00517   
00518   if (bytes == 0)
00519     return NULL;
00520 #ifdef DBUS_BUILD_TESTS
00521   else if (fail_size != 0 && bytes > fail_size)
00522     return NULL;
00523   else if (guards)
00524     {
00525       void *block;
00526 
00527       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
00528       if (block)
00529         _dbus_atomic_inc (&n_blocks_outstanding);
00530       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
00531     }
00532 #endif
00533   else
00534     {
00535       void *mem;
00536       mem = calloc (bytes, 1);
00537 #ifdef DBUS_BUILD_TESTS
00538       if (mem)
00539         _dbus_atomic_inc (&n_blocks_outstanding);
00540 #endif
00541       return mem;
00542     }
00543 }
00544 
00555 void*
00556 dbus_realloc (void  *memory,
00557               size_t bytes)
00558 {
00559 #ifdef DBUS_BUILD_TESTS
00560   _dbus_initialize_malloc_debug ();
00561   
00562   if (_dbus_decrement_fail_alloc_counter ())
00563     {
00564       _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
00565       
00566       return NULL;
00567     }
00568 #endif
00569   
00570   if (bytes == 0) /* guarantee this is safe */
00571     {
00572       dbus_free (memory);
00573       return NULL;
00574     }
00575 #ifdef DBUS_BUILD_TESTS
00576   else if (fail_size != 0 && bytes > fail_size)
00577     return NULL;
00578   else if (guards)
00579     {
00580       if (memory)
00581         {
00582           size_t old_bytes;
00583           void *block;
00584           
00585           check_guards (memory, FALSE);
00586           
00587           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
00588                            bytes + GUARD_EXTRA_SIZE);
00589 
00590           old_bytes = *(dbus_uint32_t*)block;
00591           if (block && bytes >= old_bytes)
00592             /* old guards shouldn't have moved */
00593             check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
00594           
00595           return set_guards (block, bytes, SOURCE_REALLOC);
00596         }
00597       else
00598         {
00599           void *block;
00600           
00601           block = malloc (bytes + GUARD_EXTRA_SIZE);
00602 
00603           if (block)
00604             _dbus_atomic_inc (&n_blocks_outstanding);
00605           
00606           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
00607         }
00608     }
00609 #endif
00610   else
00611     {
00612       void *mem;
00613       mem = realloc (memory, bytes);
00614 #ifdef DBUS_BUILD_TESTS
00615       if (memory == NULL && mem != NULL)
00616             _dbus_atomic_inc (&n_blocks_outstanding);
00617 #endif
00618       return mem;
00619     }
00620 }
00621 
00628 void
00629 dbus_free (void  *memory)
00630 {
00631 #ifdef DBUS_BUILD_TESTS
00632   if (guards)
00633     {
00634       check_guards (memory, TRUE);
00635       if (memory)
00636         {
00637 #ifdef DBUS_DISABLE_ASSERT
00638           _dbus_atomic_dec (&n_blocks_outstanding);
00639 #else
00640           dbus_int32_t old_value;
00641 
00642           old_value = _dbus_atomic_dec (&n_blocks_outstanding);
00643           _dbus_assert (old_value >= 1);
00644 #endif
00645 
00646           free (((unsigned char*)memory) - GUARD_START_OFFSET);
00647         }
00648       
00649       return;
00650     }
00651 #endif
00652     
00653   if (memory) /* we guarantee it's safe to free (NULL) */
00654     {
00655 #ifdef DBUS_BUILD_TESTS
00656 #ifdef DBUS_DISABLE_ASSERT
00657       _dbus_atomic_dec (&n_blocks_outstanding);
00658 #else
00659       dbus_int32_t old_value;
00660 
00661       old_value = _dbus_atomic_dec (&n_blocks_outstanding);
00662       _dbus_assert (old_value >= 1);
00663 #endif
00664 #endif
00665 
00666       free (memory);
00667     }
00668 }
00669 
00676 void
00677 dbus_free_string_array (char **str_array)
00678 {
00679   if (str_array)
00680     {
00681       int i;
00682 
00683       i = 0;
00684       while (str_array[i])
00685         {
00686           dbus_free (str_array[i]);
00687           i++;
00688         }
00689 
00690       dbus_free (str_array);
00691     }
00692 }
00693  /* End of public API docs block */
00695 
00696 
00709 int _dbus_current_generation = 1;
00710 
00714 typedef struct ShutdownClosure ShutdownClosure;
00715 
00719 struct ShutdownClosure
00720 {
00721   ShutdownClosure *next;     
00722   DBusShutdownFunction func; 
00723   void *data;                
00724 };
00725 
00726 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs);
00727 static ShutdownClosure *registered_globals = NULL;
00728 
00737 dbus_bool_t
00738 _dbus_register_shutdown_func (DBusShutdownFunction  func,
00739                               void                 *data)
00740 {
00741   ShutdownClosure *c;
00742 
00743   c = dbus_new (ShutdownClosure, 1);
00744 
00745   if (c == NULL)
00746     return FALSE;
00747 
00748   c->func = func;
00749   c->data = data;
00750 
00751   _DBUS_LOCK (shutdown_funcs);
00752   
00753   c->next = registered_globals;
00754   registered_globals = c;
00755 
00756   _DBUS_UNLOCK (shutdown_funcs);
00757   
00758   return TRUE;
00759 }
00760  /* End of private API docs block */
00762 
00763 
00807 void
00808 dbus_shutdown (void)
00809 {
00810   while (registered_globals != NULL)
00811     {
00812       ShutdownClosure *c;
00813 
00814       c = registered_globals;
00815       registered_globals = c->next;
00816       
00817       (* c->func) (c->data);
00818       
00819       dbus_free (c);
00820     }
00821 
00822   _dbus_current_generation += 1;
00823 }
00824  
00827 #ifdef DBUS_BUILD_TESTS
00828 #include "dbus-test.h"
00829 
00835 dbus_bool_t
00836 _dbus_memory_test (void)
00837 {
00838   dbus_bool_t old_guards;
00839   void *p;
00840   size_t size;
00841 
00842   old_guards = guards;
00843   guards = TRUE;
00844   p = dbus_malloc (4);
00845   if (p == NULL)
00846     _dbus_assert_not_reached ("no memory");
00847   for (size = 4; size < 256; size += 4)
00848     {
00849       p = dbus_realloc (p, size);
00850       if (p == NULL)
00851         _dbus_assert_not_reached ("no memory");
00852     }
00853   for (size = 256; size != 0; size -= 4)
00854     {
00855       p = dbus_realloc (p, size);
00856       if (p == NULL)
00857         _dbus_assert_not_reached ("no memory");
00858     }
00859   dbus_free (p);
00860   guards = old_guards;
00861   return TRUE;
00862 }
00863 
00864 #endif