D-Bus  1.6.8
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 dbus_bool_t malloc_cannot_fail = FALSE;
00110 static DBusAtomic n_blocks_outstanding = {0};
00111 
00113 #define GUARD_VALUE 0xdeadbeef
00114 
00115 #define GUARD_INFO_SIZE 8
00116 
00117 #define GUARD_START_PAD 16
00118 
00119 #define GUARD_END_PAD 16
00120 
00121 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
00122 
00123 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
00124 
00125 static void
00126 _dbus_initialize_malloc_debug (void)
00127 {
00128   if (!debug_initialized)
00129     {
00130       debug_initialized = TRUE;
00131       
00132       if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
00133         {
00134           fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
00135           fail_alloc_counter = fail_nth;
00136           _dbus_verbose ("Will fail dbus_malloc every %d times\n", fail_nth);
00137         }
00138       
00139       if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
00140         {
00141           fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
00142           _dbus_verbose ("Will fail mallocs over %ld bytes\n",
00143                          (long) fail_size);
00144         }
00145 
00146       if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
00147         {
00148           guards = TRUE;
00149           _dbus_verbose ("Will use dbus_malloc guards\n");
00150         }
00151 
00152       if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
00153         {
00154           disable_mem_pools = TRUE;
00155           _dbus_verbose ("Will disable memory pools\n");
00156         }
00157 
00158       if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
00159         {
00160           backtrace_on_fail_alloc = TRUE;
00161           _dbus_verbose ("Will backtrace on failing a dbus_malloc\n");
00162         }
00163 
00164       if (_dbus_getenv ("DBUS_MALLOC_CANNOT_FAIL") != NULL)
00165         {
00166           malloc_cannot_fail = TRUE;
00167           _dbus_verbose ("Will abort if system malloc() and friends fail\n");
00168         }
00169     }
00170 }
00171 
00177 dbus_bool_t
00178 _dbus_disable_mem_pools (void)
00179 {
00180   _dbus_initialize_malloc_debug ();
00181   return disable_mem_pools;
00182 }
00183 
00192 void
00193 _dbus_set_fail_alloc_counter (int until_next_fail)
00194 {
00195   _dbus_initialize_malloc_debug ();
00196 
00197   fail_alloc_counter = until_next_fail;
00198 
00199 #if 0
00200   _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
00201 #endif
00202 }
00203 
00210 int
00211 _dbus_get_fail_alloc_counter (void)
00212 {
00213   _dbus_initialize_malloc_debug ();
00214 
00215   return fail_alloc_counter;
00216 }
00217 
00224 void
00225 _dbus_set_fail_alloc_failures (int failures_per_failure)
00226 {
00227   n_failures_per_failure = failures_per_failure;
00228 }
00229 
00236 int
00237 _dbus_get_fail_alloc_failures (void)
00238 {
00239   return n_failures_per_failure;
00240 }
00241 
00242 #ifdef DBUS_BUILD_TESTS
00243 
00251 dbus_bool_t
00252 _dbus_decrement_fail_alloc_counter (void)
00253 {
00254   _dbus_initialize_malloc_debug ();
00255 #ifdef DBUS_WIN_FIXME
00256   {
00257     static dbus_bool_t called = 0;
00258 
00259     if (!called)
00260       {
00261         _dbus_verbose("TODO: memory allocation testing errors disabled for now\n");
00262         called = 1;
00263       }
00264     return FALSE;
00265   }
00266 #endif
00267 
00268   if (fail_alloc_counter <= 0)
00269     {
00270       if (backtrace_on_fail_alloc)
00271         _dbus_print_backtrace ();
00272 
00273       _dbus_verbose ("failure %d\n", n_failures_this_failure);
00274       
00275       n_failures_this_failure += 1;
00276       if (n_failures_this_failure >= n_failures_per_failure)
00277         {
00278           if (fail_nth >= 0)
00279             fail_alloc_counter = fail_nth;
00280           else
00281             fail_alloc_counter = _DBUS_INT_MAX;
00282 
00283           n_failures_this_failure = 0;
00284 
00285           _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
00286         }
00287       
00288       return TRUE;
00289     }
00290   else
00291     {
00292       fail_alloc_counter -= 1;
00293       return FALSE;
00294     }
00295 }
00296 #endif /* DBUS_BUILD_TESTS */
00297 
00303 int
00304 _dbus_get_malloc_blocks_outstanding (void)
00305 {
00306   return _dbus_atomic_get (&n_blocks_outstanding);
00307 }
00308 
00312 typedef enum
00313 {
00314   SOURCE_UNKNOWN,
00315   SOURCE_MALLOC,
00316   SOURCE_REALLOC,
00317   SOURCE_MALLOC_ZERO,
00318   SOURCE_REALLOC_NULL
00319 } BlockSource;
00320 
00321 static const char*
00322 source_string (BlockSource source)
00323 {
00324   switch (source)
00325     {
00326     case SOURCE_UNKNOWN:
00327       return "unknown";
00328     case SOURCE_MALLOC:
00329       return "malloc";
00330     case SOURCE_REALLOC:
00331       return "realloc";
00332     case SOURCE_MALLOC_ZERO:
00333       return "malloc0";
00334     case SOURCE_REALLOC_NULL:
00335       return "realloc(NULL)";
00336     }
00337   _dbus_assert_not_reached ("Invalid malloc block source ID");
00338   return "invalid!";
00339 }
00340 
00341 static void
00342 check_guards (void       *free_block,
00343               dbus_bool_t overwrite)
00344 {
00345   if (free_block != NULL)
00346     {
00347       unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
00348       size_t requested_bytes = *(dbus_uint32_t*)block;
00349       BlockSource source = *(dbus_uint32_t*)(block + 4);
00350       unsigned int i;
00351       dbus_bool_t failed;
00352 
00353       failed = FALSE;
00354 
00355 #if 0
00356       _dbus_verbose ("Checking %d bytes request from source %s\n",
00357                      requested_bytes, source_string (source));
00358 #endif
00359       
00360       i = GUARD_INFO_SIZE;
00361       while (i < GUARD_START_OFFSET)
00362         {
00363           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00364           if (value != GUARD_VALUE)
00365             {
00366               _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n",
00367                           (long) requested_bytes, source_string (source),
00368                           value, i, GUARD_VALUE);
00369               failed = TRUE;
00370             }
00371           
00372           i += 4;
00373         }
00374 
00375       i = GUARD_START_OFFSET + requested_bytes;
00376       while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00377         {
00378           dbus_uint32_t value = *(dbus_uint32_t*) &block[i];
00379           if (value != GUARD_VALUE)
00380             {
00381               _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n",
00382                           (long) requested_bytes, source_string (source),
00383                           value, i, GUARD_VALUE);
00384               failed = TRUE;
00385             }
00386           
00387           i += 4;
00388         }
00389 
00390       /* set memory to anything but nul bytes */
00391       if (overwrite)
00392         memset (free_block, 'g', requested_bytes);
00393       
00394       if (failed)
00395         _dbus_assert_not_reached ("guard value corruption");
00396     }
00397 }
00398 
00399 static void*
00400 set_guards (void       *real_block,
00401             size_t      requested_bytes,
00402             BlockSource source)
00403 {
00404   unsigned char *block = real_block;
00405   unsigned int i;
00406   
00407   if (block == NULL)
00408     return NULL;
00409 
00410   _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
00411   
00412   *((dbus_uint32_t*)block) = requested_bytes;
00413   *((dbus_uint32_t*)(block + 4)) = source;
00414 
00415   i = GUARD_INFO_SIZE;
00416   while (i < GUARD_START_OFFSET)
00417     {
00418       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00419       
00420       i += 4;
00421     }
00422 
00423   i = GUARD_START_OFFSET + requested_bytes;
00424   while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
00425     {
00426       (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE;
00427       
00428       i += 4;
00429     }
00430   
00431   check_guards (block + GUARD_START_OFFSET, FALSE);
00432   
00433   return block + GUARD_START_OFFSET;
00434 }
00435 
00436 #endif
00437  /* End of internals docs */
00439 
00440 
00459 void*
00460 dbus_malloc (size_t bytes)
00461 {
00462 #ifdef DBUS_BUILD_TESTS
00463   _dbus_initialize_malloc_debug ();
00464   
00465   if (_dbus_decrement_fail_alloc_counter ())
00466     {
00467       _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
00468       return NULL;
00469     }
00470 #endif
00471 
00472   if (bytes == 0) /* some system mallocs handle this, some don't */
00473     return NULL;
00474 #ifdef DBUS_BUILD_TESTS
00475   else if (fail_size != 0 && bytes > fail_size)
00476     return NULL;
00477   else if (guards)
00478     {
00479       void *block;
00480 
00481       block = malloc (bytes + GUARD_EXTRA_SIZE);
00482       if (block)
00483         {
00484           _dbus_atomic_inc (&n_blocks_outstanding);
00485         }
00486       else if (malloc_cannot_fail)
00487         {
00488           _dbus_warn ("out of memory: malloc (%ld + %ld)\n",
00489               (long) bytes, (long) GUARD_EXTRA_SIZE);
00490           _dbus_abort ();
00491         }
00492       
00493       return set_guards (block, bytes, SOURCE_MALLOC);
00494     }
00495 #endif
00496   else
00497     {
00498       void *mem;
00499       mem = malloc (bytes);
00500 
00501 #ifdef DBUS_BUILD_TESTS
00502       if (mem)
00503         {
00504           _dbus_atomic_inc (&n_blocks_outstanding);
00505         }
00506       else if (malloc_cannot_fail)
00507         {
00508           _dbus_warn ("out of memory: malloc (%ld)\n", (long) bytes);
00509           _dbus_abort ();
00510         }
00511 #endif
00512 
00513       return mem;
00514     }
00515 }
00516 
00529 void*
00530 dbus_malloc0 (size_t bytes)
00531 {
00532 #ifdef DBUS_BUILD_TESTS
00533   _dbus_initialize_malloc_debug ();
00534   
00535   if (_dbus_decrement_fail_alloc_counter ())
00536     {
00537       _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
00538       
00539       return NULL;
00540     }
00541 #endif
00542   
00543   if (bytes == 0)
00544     return NULL;
00545 #ifdef DBUS_BUILD_TESTS
00546   else if (fail_size != 0 && bytes > fail_size)
00547     return NULL;
00548   else if (guards)
00549     {
00550       void *block;
00551 
00552       block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
00553 
00554       if (block)
00555         {
00556           _dbus_atomic_inc (&n_blocks_outstanding);
00557         }
00558       else if (malloc_cannot_fail)
00559         {
00560           _dbus_warn ("out of memory: calloc (%ld + %ld, 1)\n",
00561               (long) bytes, (long) GUARD_EXTRA_SIZE);
00562           _dbus_abort ();
00563         }
00564 
00565       return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
00566     }
00567 #endif
00568   else
00569     {
00570       void *mem;
00571       mem = calloc (bytes, 1);
00572 
00573 #ifdef DBUS_BUILD_TESTS
00574       if (mem)
00575         {
00576           _dbus_atomic_inc (&n_blocks_outstanding);
00577         }
00578       else if (malloc_cannot_fail)
00579         {
00580           _dbus_warn ("out of memory: calloc (%ld)\n", (long) bytes);
00581           _dbus_abort ();
00582         }
00583 #endif
00584 
00585       return mem;
00586     }
00587 }
00588 
00599 void*
00600 dbus_realloc (void  *memory,
00601               size_t bytes)
00602 {
00603 #ifdef DBUS_BUILD_TESTS
00604   _dbus_initialize_malloc_debug ();
00605   
00606   if (_dbus_decrement_fail_alloc_counter ())
00607     {
00608       _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
00609       
00610       return NULL;
00611     }
00612 #endif
00613   
00614   if (bytes == 0) /* guarantee this is safe */
00615     {
00616       dbus_free (memory);
00617       return NULL;
00618     }
00619 #ifdef DBUS_BUILD_TESTS
00620   else if (fail_size != 0 && bytes > fail_size)
00621     return NULL;
00622   else if (guards)
00623     {
00624       if (memory)
00625         {
00626           size_t old_bytes;
00627           void *block;
00628           
00629           check_guards (memory, FALSE);
00630           
00631           block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
00632                            bytes + GUARD_EXTRA_SIZE);
00633 
00634           if (block == NULL)
00635             {
00636               if (malloc_cannot_fail)
00637                 {
00638                   _dbus_warn ("out of memory: realloc (%p, %ld + %ld)\n",
00639                       memory, (long) bytes, (long) GUARD_EXTRA_SIZE);
00640                   _dbus_abort ();
00641                 }
00642 
00643               return NULL;
00644             }
00645 
00646           old_bytes = *(dbus_uint32_t*)block;
00647           if (bytes >= old_bytes)
00648             /* old guards shouldn't have moved */
00649             check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
00650           
00651           return set_guards (block, bytes, SOURCE_REALLOC);
00652         }
00653       else
00654         {
00655           void *block;
00656           
00657           block = malloc (bytes + GUARD_EXTRA_SIZE);
00658 
00659           if (block)
00660             {
00661               _dbus_atomic_inc (&n_blocks_outstanding);
00662             }
00663           else if (malloc_cannot_fail)
00664             {
00665               _dbus_warn ("out of memory: malloc (%ld + %ld)\n",
00666                   (long) bytes, (long) GUARD_EXTRA_SIZE);
00667               _dbus_abort ();
00668             }
00669 
00670           return set_guards (block, bytes, SOURCE_REALLOC_NULL);   
00671         }
00672     }
00673 #endif
00674   else
00675     {
00676       void *mem;
00677       mem = realloc (memory, bytes);
00678 
00679 #ifdef DBUS_BUILD_TESTS
00680       if (mem == NULL && malloc_cannot_fail)
00681         {
00682           _dbus_warn ("out of memory: malloc (%ld)\n", (long) bytes);
00683           _dbus_abort ();
00684         }
00685 
00686       if (memory == NULL && mem != NULL)
00687             _dbus_atomic_inc (&n_blocks_outstanding);
00688 #endif
00689       return mem;
00690     }
00691 }
00692 
00699 void
00700 dbus_free (void  *memory)
00701 {
00702 #ifdef DBUS_BUILD_TESTS
00703   if (guards)
00704     {
00705       check_guards (memory, TRUE);
00706       if (memory)
00707         {
00708 #ifdef DBUS_DISABLE_ASSERT
00709           _dbus_atomic_dec (&n_blocks_outstanding);
00710 #else
00711           dbus_int32_t old_value;
00712 
00713           old_value = _dbus_atomic_dec (&n_blocks_outstanding);
00714           _dbus_assert (old_value >= 1);
00715 #endif
00716 
00717           free (((unsigned char*)memory) - GUARD_START_OFFSET);
00718         }
00719       
00720       return;
00721     }
00722 #endif
00723     
00724   if (memory) /* we guarantee it's safe to free (NULL) */
00725     {
00726 #ifdef DBUS_BUILD_TESTS
00727 #ifdef DBUS_DISABLE_ASSERT
00728       _dbus_atomic_dec (&n_blocks_outstanding);
00729 #else
00730       dbus_int32_t old_value;
00731 
00732       old_value = _dbus_atomic_dec (&n_blocks_outstanding);
00733       _dbus_assert (old_value >= 1);
00734 #endif
00735 #endif
00736 
00737       free (memory);
00738     }
00739 }
00740 
00747 void
00748 dbus_free_string_array (char **str_array)
00749 {
00750   if (str_array)
00751     {
00752       int i;
00753 
00754       i = 0;
00755       while (str_array[i])
00756         {
00757           dbus_free (str_array[i]);
00758           i++;
00759         }
00760 
00761       dbus_free (str_array);
00762     }
00763 }
00764  /* End of public API docs block */
00766 
00767 
00780 int _dbus_current_generation = 1;
00781 
00785 typedef struct ShutdownClosure ShutdownClosure;
00786 
00790 struct ShutdownClosure
00791 {
00792   ShutdownClosure *next;     
00793   DBusShutdownFunction func; 
00794   void *data;                
00795 };
00796 
00797 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs);
00798 static ShutdownClosure *registered_globals = NULL;
00799 
00808 dbus_bool_t
00809 _dbus_register_shutdown_func (DBusShutdownFunction  func,
00810                               void                 *data)
00811 {
00812   ShutdownClosure *c;
00813 
00814   c = dbus_new (ShutdownClosure, 1);
00815 
00816   if (c == NULL)
00817     return FALSE;
00818 
00819   c->func = func;
00820   c->data = data;
00821 
00822   _DBUS_LOCK (shutdown_funcs);
00823   
00824   c->next = registered_globals;
00825   registered_globals = c;
00826 
00827   _DBUS_UNLOCK (shutdown_funcs);
00828   
00829   return TRUE;
00830 }
00831  /* End of private API docs block */
00833 
00834 
00878 void
00879 dbus_shutdown (void)
00880 {
00881   while (registered_globals != NULL)
00882     {
00883       ShutdownClosure *c;
00884 
00885       c = registered_globals;
00886       registered_globals = c->next;
00887       
00888       (* c->func) (c->data);
00889       
00890       dbus_free (c);
00891     }
00892 
00893   _dbus_current_generation += 1;
00894 }
00895  
00898 #ifdef DBUS_BUILD_TESTS
00899 #include "dbus-test.h"
00900 
00906 dbus_bool_t
00907 _dbus_memory_test (void)
00908 {
00909   dbus_bool_t old_guards;
00910   void *p;
00911   size_t size;
00912 
00913   old_guards = guards;
00914   guards = TRUE;
00915   p = dbus_malloc (4);
00916   if (p == NULL)
00917     _dbus_assert_not_reached ("no memory");
00918   for (size = 4; size < 256; size += 4)
00919     {
00920       p = dbus_realloc (p, size);
00921       if (p == NULL)
00922         _dbus_assert_not_reached ("no memory");
00923     }
00924   for (size = 256; size != 0; size -= 4)
00925     {
00926       p = dbus_realloc (p, size);
00927       if (p == NULL)
00928         _dbus_assert_not_reached ("no memory");
00929     }
00930   dbus_free (p);
00931   guards = old_guards;
00932   return TRUE;
00933 }
00934 
00935 #endif