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