D-Bus
1.4.10
|
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 n_blocks_outstanding.value; 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 _dbus_atomic_dec (&n_blocks_outstanding); 00638 00639 _dbus_assert (n_blocks_outstanding.value >= 0); 00640 00641 free (((unsigned char*)memory) - GUARD_START_OFFSET); 00642 } 00643 00644 return; 00645 } 00646 #endif 00647 00648 if (memory) /* we guarantee it's safe to free (NULL) */ 00649 { 00650 #ifdef DBUS_BUILD_TESTS 00651 _dbus_atomic_dec (&n_blocks_outstanding); 00652 00653 _dbus_assert (n_blocks_outstanding.value >= 0); 00654 #endif 00655 00656 free (memory); 00657 } 00658 } 00659 00666 void 00667 dbus_free_string_array (char **str_array) 00668 { 00669 if (str_array) 00670 { 00671 int i; 00672 00673 i = 0; 00674 while (str_array[i]) 00675 { 00676 dbus_free (str_array[i]); 00677 i++; 00678 } 00679 00680 dbus_free (str_array); 00681 } 00682 } 00683 /* End of public API docs block */ 00685 00686 00699 int _dbus_current_generation = 1; 00700 00704 typedef struct ShutdownClosure ShutdownClosure; 00705 00709 struct ShutdownClosure 00710 { 00711 ShutdownClosure *next; 00712 DBusShutdownFunction func; 00713 void *data; 00714 }; 00715 00716 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs); 00717 static ShutdownClosure *registered_globals = NULL; 00718 00727 dbus_bool_t 00728 _dbus_register_shutdown_func (DBusShutdownFunction func, 00729 void *data) 00730 { 00731 ShutdownClosure *c; 00732 00733 c = dbus_new (ShutdownClosure, 1); 00734 00735 if (c == NULL) 00736 return FALSE; 00737 00738 c->func = func; 00739 c->data = data; 00740 00741 _DBUS_LOCK (shutdown_funcs); 00742 00743 c->next = registered_globals; 00744 registered_globals = c; 00745 00746 _DBUS_UNLOCK (shutdown_funcs); 00747 00748 return TRUE; 00749 } 00750 /* End of private API docs block */ 00752 00753 00797 void 00798 dbus_shutdown (void) 00799 { 00800 while (registered_globals != NULL) 00801 { 00802 ShutdownClosure *c; 00803 00804 c = registered_globals; 00805 registered_globals = c->next; 00806 00807 (* c->func) (c->data); 00808 00809 dbus_free (c); 00810 } 00811 00812 _dbus_current_generation += 1; 00813 } 00814 00817 #ifdef DBUS_BUILD_TESTS 00818 #include "dbus-test.h" 00819 00825 dbus_bool_t 00826 _dbus_memory_test (void) 00827 { 00828 dbus_bool_t old_guards; 00829 void *p; 00830 size_t size; 00831 00832 old_guards = guards; 00833 guards = TRUE; 00834 p = dbus_malloc (4); 00835 if (p == NULL) 00836 _dbus_assert_not_reached ("no memory"); 00837 for (size = 4; size < 256; size += 4) 00838 { 00839 p = dbus_realloc (p, size); 00840 if (p == NULL) 00841 _dbus_assert_not_reached ("no memory"); 00842 } 00843 for (size = 256; size != 0; size -= 4) 00844 { 00845 p = dbus_realloc (p, size); 00846 if (p == NULL) 00847 _dbus_assert_not_reached ("no memory"); 00848 } 00849 dbus_free (p); 00850 guards = old_guards; 00851 return TRUE; 00852 } 00853 00854 #endif