D-Bus
1.10.12
|
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