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