D-Bus 1.2.24
|
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 "dbus-memory.h" 00025 #include "dbus-internals.h" 00026 #include "dbus-sysdeps.h" 00027 #include "dbus-list.h" 00028 #include <stdlib.h> 00029 /* end of public API docs */ 00091 00098 #ifdef DBUS_BUILD_TESTS 00099 static dbus_bool_t debug_initialized = FALSE; 00100 static int fail_nth = -1; 00101 static size_t fail_size = 0; 00102 static int fail_alloc_counter = _DBUS_INT_MAX; 00103 static int n_failures_per_failure = 1; 00104 static int n_failures_this_failure = 0; 00105 static dbus_bool_t guards = FALSE; 00106 static dbus_bool_t disable_mem_pools = FALSE; 00107 static dbus_bool_t backtrace_on_fail_alloc = FALSE; 00108 static DBusAtomic n_blocks_outstanding = {0}; 00109 00111 #define GUARD_VALUE 0xdeadbeef 00112 00113 #define GUARD_INFO_SIZE 8 00114 00115 #define GUARD_START_PAD 16 00116 00117 #define GUARD_END_PAD 16 00118 00119 #define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE) 00120 00121 #define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD) 00122 00123 static void 00124 _dbus_initialize_malloc_debug (void) 00125 { 00126 if (!debug_initialized) 00127 { 00128 debug_initialized = TRUE; 00129 00130 if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL) 00131 { 00132 fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH")); 00133 fail_alloc_counter = fail_nth; 00134 _dbus_verbose ("Will fail malloc every %d times\n", fail_nth); 00135 } 00136 00137 if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL) 00138 { 00139 fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN")); 00140 _dbus_verbose ("Will fail mallocs over %ld bytes\n", 00141 (long) fail_size); 00142 } 00143 00144 if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL) 00145 { 00146 guards = TRUE; 00147 _dbus_verbose ("Will use malloc guards\n"); 00148 } 00149 00150 if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL) 00151 { 00152 disable_mem_pools = TRUE; 00153 _dbus_verbose ("Will disable memory pools\n"); 00154 } 00155 00156 if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL) 00157 { 00158 backtrace_on_fail_alloc = TRUE; 00159 _dbus_verbose ("Will backtrace on failing a malloc\n"); 00160 } 00161 } 00162 } 00163 00169 dbus_bool_t 00170 _dbus_disable_mem_pools (void) 00171 { 00172 _dbus_initialize_malloc_debug (); 00173 return disable_mem_pools; 00174 } 00175 00184 void 00185 _dbus_set_fail_alloc_counter (int until_next_fail) 00186 { 00187 _dbus_initialize_malloc_debug (); 00188 00189 fail_alloc_counter = until_next_fail; 00190 00191 #if 0 00192 _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter); 00193 #endif 00194 } 00195 00202 int 00203 _dbus_get_fail_alloc_counter (void) 00204 { 00205 _dbus_initialize_malloc_debug (); 00206 00207 return fail_alloc_counter; 00208 } 00209 00216 void 00217 _dbus_set_fail_alloc_failures (int failures_per_failure) 00218 { 00219 n_failures_per_failure = failures_per_failure; 00220 } 00221 00228 int 00229 _dbus_get_fail_alloc_failures (void) 00230 { 00231 return n_failures_per_failure; 00232 } 00233 00234 #ifdef DBUS_BUILD_TESTS 00235 00243 dbus_bool_t 00244 _dbus_decrement_fail_alloc_counter (void) 00245 { 00246 _dbus_initialize_malloc_debug (); 00247 00248 if (fail_alloc_counter <= 0) 00249 { 00250 if (backtrace_on_fail_alloc) 00251 _dbus_print_backtrace (); 00252 00253 _dbus_verbose ("failure %d\n", n_failures_this_failure); 00254 00255 n_failures_this_failure += 1; 00256 if (n_failures_this_failure >= n_failures_per_failure) 00257 { 00258 if (fail_nth >= 0) 00259 fail_alloc_counter = fail_nth; 00260 else 00261 fail_alloc_counter = _DBUS_INT_MAX; 00262 00263 n_failures_this_failure = 0; 00264 00265 _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter); 00266 } 00267 00268 return TRUE; 00269 } 00270 else 00271 { 00272 fail_alloc_counter -= 1; 00273 return FALSE; 00274 } 00275 } 00276 #endif /* DBUS_BUILD_TESTS */ 00277 00283 int 00284 _dbus_get_malloc_blocks_outstanding (void) 00285 { 00286 return n_blocks_outstanding.value; 00287 } 00288 00292 typedef enum 00293 { 00294 SOURCE_UNKNOWN, 00295 SOURCE_MALLOC, 00296 SOURCE_REALLOC, 00297 SOURCE_MALLOC_ZERO, 00298 SOURCE_REALLOC_NULL 00299 } BlockSource; 00300 00301 static const char* 00302 source_string (BlockSource source) 00303 { 00304 switch (source) 00305 { 00306 case SOURCE_UNKNOWN: 00307 return "unknown"; 00308 case SOURCE_MALLOC: 00309 return "malloc"; 00310 case SOURCE_REALLOC: 00311 return "realloc"; 00312 case SOURCE_MALLOC_ZERO: 00313 return "malloc0"; 00314 case SOURCE_REALLOC_NULL: 00315 return "realloc(NULL)"; 00316 } 00317 _dbus_assert_not_reached ("Invalid malloc block source ID"); 00318 return "invalid!"; 00319 } 00320 00321 static void 00322 check_guards (void *free_block, 00323 dbus_bool_t overwrite) 00324 { 00325 if (free_block != NULL) 00326 { 00327 unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET; 00328 size_t requested_bytes = *(dbus_uint32_t*)block; 00329 BlockSource source = *(dbus_uint32_t*)(block + 4); 00330 unsigned int i; 00331 dbus_bool_t failed; 00332 00333 failed = FALSE; 00334 00335 #if 0 00336 _dbus_verbose ("Checking %d bytes request from source %s\n", 00337 requested_bytes, source_string (source)); 00338 #endif 00339 00340 i = GUARD_INFO_SIZE; 00341 while (i < GUARD_START_OFFSET) 00342 { 00343 dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; 00344 if (value != GUARD_VALUE) 00345 { 00346 _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x\n", 00347 (long) requested_bytes, source_string (source), 00348 value, i, GUARD_VALUE); 00349 failed = TRUE; 00350 } 00351 00352 i += 4; 00353 } 00354 00355 i = GUARD_START_OFFSET + requested_bytes; 00356 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) 00357 { 00358 dbus_uint32_t value = *(dbus_uint32_t*) &block[i]; 00359 if (value != GUARD_VALUE) 00360 { 00361 _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x\n", 00362 (long) requested_bytes, source_string (source), 00363 value, i, GUARD_VALUE); 00364 failed = TRUE; 00365 } 00366 00367 i += 4; 00368 } 00369 00370 /* set memory to anything but nul bytes */ 00371 if (overwrite) 00372 memset (free_block, 'g', requested_bytes); 00373 00374 if (failed) 00375 _dbus_assert_not_reached ("guard value corruption"); 00376 } 00377 } 00378 00379 static void* 00380 set_guards (void *real_block, 00381 size_t requested_bytes, 00382 BlockSource source) 00383 { 00384 unsigned char *block = real_block; 00385 unsigned int i; 00386 00387 if (block == NULL) 00388 return NULL; 00389 00390 _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE); 00391 00392 *((dbus_uint32_t*)block) = requested_bytes; 00393 *((dbus_uint32_t*)(block + 4)) = source; 00394 00395 i = GUARD_INFO_SIZE; 00396 while (i < GUARD_START_OFFSET) 00397 { 00398 (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; 00399 00400 i += 4; 00401 } 00402 00403 i = GUARD_START_OFFSET + requested_bytes; 00404 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD)) 00405 { 00406 (*(dbus_uint32_t*) &block[i]) = GUARD_VALUE; 00407 00408 i += 4; 00409 } 00410 00411 check_guards (block + GUARD_START_OFFSET, FALSE); 00412 00413 return block + GUARD_START_OFFSET; 00414 } 00415 00416 #endif 00417 /* End of internals docs */ 00419 00420 00439 void* 00440 dbus_malloc (size_t bytes) 00441 { 00442 #ifdef DBUS_BUILD_TESTS 00443 _dbus_initialize_malloc_debug (); 00444 00445 if (_dbus_decrement_fail_alloc_counter ()) 00446 { 00447 _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes); 00448 return NULL; 00449 } 00450 #endif 00451 00452 if (bytes == 0) /* some system mallocs handle this, some don't */ 00453 return NULL; 00454 #ifdef DBUS_BUILD_TESTS 00455 else if (fail_size != 0 && bytes > fail_size) 00456 return NULL; 00457 else if (guards) 00458 { 00459 void *block; 00460 00461 block = malloc (bytes + GUARD_EXTRA_SIZE); 00462 if (block) 00463 _dbus_atomic_inc (&n_blocks_outstanding); 00464 00465 return set_guards (block, bytes, SOURCE_MALLOC); 00466 } 00467 #endif 00468 else 00469 { 00470 void *mem; 00471 mem = malloc (bytes); 00472 #ifdef DBUS_BUILD_TESTS 00473 if (mem) 00474 _dbus_atomic_inc (&n_blocks_outstanding); 00475 #endif 00476 return mem; 00477 } 00478 } 00479 00492 void* 00493 dbus_malloc0 (size_t bytes) 00494 { 00495 #ifdef DBUS_BUILD_TESTS 00496 _dbus_initialize_malloc_debug (); 00497 00498 if (_dbus_decrement_fail_alloc_counter ()) 00499 { 00500 _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes); 00501 00502 return NULL; 00503 } 00504 #endif 00505 00506 if (bytes == 0) 00507 return NULL; 00508 #ifdef DBUS_BUILD_TESTS 00509 else if (fail_size != 0 && bytes > fail_size) 00510 return NULL; 00511 else if (guards) 00512 { 00513 void *block; 00514 00515 block = calloc (bytes + GUARD_EXTRA_SIZE, 1); 00516 if (block) 00517 _dbus_atomic_inc (&n_blocks_outstanding); 00518 return set_guards (block, bytes, SOURCE_MALLOC_ZERO); 00519 } 00520 #endif 00521 else 00522 { 00523 void *mem; 00524 mem = calloc (bytes, 1); 00525 #ifdef DBUS_BUILD_TESTS 00526 if (mem) 00527 _dbus_atomic_inc (&n_blocks_outstanding); 00528 #endif 00529 return mem; 00530 } 00531 } 00532 00543 void* 00544 dbus_realloc (void *memory, 00545 size_t bytes) 00546 { 00547 #ifdef DBUS_BUILD_TESTS 00548 _dbus_initialize_malloc_debug (); 00549 00550 if (_dbus_decrement_fail_alloc_counter ()) 00551 { 00552 _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes); 00553 00554 return NULL; 00555 } 00556 #endif 00557 00558 if (bytes == 0) /* guarantee this is safe */ 00559 { 00560 dbus_free (memory); 00561 return NULL; 00562 } 00563 #ifdef DBUS_BUILD_TESTS 00564 else if (fail_size != 0 && bytes > fail_size) 00565 return NULL; 00566 else if (guards) 00567 { 00568 if (memory) 00569 { 00570 size_t old_bytes; 00571 void *block; 00572 00573 check_guards (memory, FALSE); 00574 00575 block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET, 00576 bytes + GUARD_EXTRA_SIZE); 00577 00578 old_bytes = *(dbus_uint32_t*)block; 00579 if (block && bytes >= old_bytes) 00580 /* old guards shouldn't have moved */ 00581 check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE); 00582 00583 return set_guards (block, bytes, SOURCE_REALLOC); 00584 } 00585 else 00586 { 00587 void *block; 00588 00589 block = malloc (bytes + GUARD_EXTRA_SIZE); 00590 00591 if (block) 00592 _dbus_atomic_inc (&n_blocks_outstanding); 00593 00594 return set_guards (block, bytes, SOURCE_REALLOC_NULL); 00595 } 00596 } 00597 #endif 00598 else 00599 { 00600 void *mem; 00601 mem = realloc (memory, bytes); 00602 #ifdef DBUS_BUILD_TESTS 00603 if (memory == NULL && mem != NULL) 00604 _dbus_atomic_inc (&n_blocks_outstanding); 00605 #endif 00606 return mem; 00607 } 00608 } 00609 00616 void 00617 dbus_free (void *memory) 00618 { 00619 #ifdef DBUS_BUILD_TESTS 00620 if (guards) 00621 { 00622 check_guards (memory, TRUE); 00623 if (memory) 00624 { 00625 _dbus_atomic_dec (&n_blocks_outstanding); 00626 00627 _dbus_assert (n_blocks_outstanding.value >= 0); 00628 00629 free (((unsigned char*)memory) - GUARD_START_OFFSET); 00630 } 00631 00632 return; 00633 } 00634 #endif 00635 00636 if (memory) /* we guarantee it's safe to free (NULL) */ 00637 { 00638 #ifdef DBUS_BUILD_TESTS 00639 _dbus_atomic_dec (&n_blocks_outstanding); 00640 00641 _dbus_assert (n_blocks_outstanding.value >= 0); 00642 #endif 00643 00644 free (memory); 00645 } 00646 } 00647 00654 void 00655 dbus_free_string_array (char **str_array) 00656 { 00657 if (str_array) 00658 { 00659 int i; 00660 00661 i = 0; 00662 while (str_array[i]) 00663 { 00664 dbus_free (str_array[i]); 00665 i++; 00666 } 00667 00668 dbus_free (str_array); 00669 } 00670 } 00671 /* End of public API docs block */ 00673 00674 00687 int _dbus_current_generation = 1; 00688 00692 typedef struct ShutdownClosure ShutdownClosure; 00693 00697 struct ShutdownClosure 00698 { 00699 ShutdownClosure *next; 00700 DBusShutdownFunction func; 00701 void *data; 00702 }; 00703 00704 _DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs); 00705 static ShutdownClosure *registered_globals = NULL; 00706 00715 dbus_bool_t 00716 _dbus_register_shutdown_func (DBusShutdownFunction func, 00717 void *data) 00718 { 00719 ShutdownClosure *c; 00720 00721 c = dbus_new (ShutdownClosure, 1); 00722 00723 if (c == NULL) 00724 return FALSE; 00725 00726 c->func = func; 00727 c->data = data; 00728 00729 _DBUS_LOCK (shutdown_funcs); 00730 00731 c->next = registered_globals; 00732 registered_globals = c; 00733 00734 _DBUS_UNLOCK (shutdown_funcs); 00735 00736 return TRUE; 00737 } 00738 /* End of private API docs block */ 00740 00741 00785 void 00786 dbus_shutdown (void) 00787 { 00788 while (registered_globals != NULL) 00789 { 00790 ShutdownClosure *c; 00791 00792 c = registered_globals; 00793 registered_globals = c->next; 00794 00795 (* c->func) (c->data); 00796 00797 dbus_free (c); 00798 } 00799 00800 _dbus_current_generation += 1; 00801 } 00802 00805 #ifdef DBUS_BUILD_TESTS 00806 #include "dbus-test.h" 00807 00813 dbus_bool_t 00814 _dbus_memory_test (void) 00815 { 00816 dbus_bool_t old_guards; 00817 void *p; 00818 size_t size; 00819 00820 old_guards = guards; 00821 guards = TRUE; 00822 p = dbus_malloc (4); 00823 if (p == NULL) 00824 _dbus_assert_not_reached ("no memory"); 00825 for (size = 4; size < 256; size += 4) 00826 { 00827 p = dbus_realloc (p, size); 00828 if (p == NULL) 00829 _dbus_assert_not_reached ("no memory"); 00830 } 00831 for (size = 256; size != 0; size -= 4) 00832 { 00833 p = dbus_realloc (p, size); 00834 if (p == NULL) 00835 _dbus_assert_not_reached ("no memory"); 00836 } 00837 dbus_free (p); 00838 guards = old_guards; 00839 return TRUE; 00840 } 00841 00842 #endif