D-Bus 1.15.12
dbus-memory.c
1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* dbus-memory.c D-Bus memory handling
3 *
4 * Copyright (C) 2002, 2003 Red Hat Inc.
5 *
6 * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 */
25
26#include <config.h>
27#include "dbus-memory.h"
28#include "dbus-internals.h"
29#include "dbus-sysdeps.h"
30#include "dbus-list.h"
31#include "dbus-threads.h"
32#include <dbus/dbus-test-tap.h>
33#include <stdlib.h>
34
/* end of public API docs */
96
103#ifdef DBUS_ENABLE_EMBEDDED_TESTS
104/* Test-only, does not need to be thread-safe */
105static dbus_bool_t debug_initialized = FALSE;
106static int fail_nth = -1;
107static size_t fail_size = 0;
108static int fail_alloc_counter = _DBUS_INT_MAX;
109static int n_failures_per_failure = 1;
110static int n_failures_this_failure = 0;
111static dbus_bool_t guards = FALSE;
112static dbus_bool_t disable_mem_pools = FALSE;
113static dbus_bool_t backtrace_on_fail_alloc = FALSE;
114static dbus_bool_t malloc_cannot_fail = FALSE;
115static DBusAtomic n_blocks_outstanding = {0};
116
118#define GUARD_VALUE 0xdeadbeef
120#define GUARD_INFO_SIZE 8
122#define GUARD_START_PAD 16
124#define GUARD_END_PAD 16
126#define GUARD_START_OFFSET (GUARD_START_PAD + GUARD_INFO_SIZE)
128#define GUARD_EXTRA_SIZE (GUARD_START_OFFSET + GUARD_END_PAD)
129
130static void
131_dbus_initialize_malloc_debug (void)
132{
133 if (!debug_initialized)
134 {
135 debug_initialized = TRUE;
136
137 if (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH") != NULL)
138 {
139 fail_nth = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_NTH"));
140 fail_alloc_counter = fail_nth;
141 _dbus_verbose ("Will fail dbus_malloc every %d times\n", fail_nth);
142 }
143
144 if (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN") != NULL)
145 {
146 fail_size = atoi (_dbus_getenv ("DBUS_MALLOC_FAIL_GREATER_THAN"));
147 _dbus_verbose ("Will fail mallocs over %ld bytes\n",
148 (long) fail_size);
149 }
150
151 if (_dbus_getenv ("DBUS_MALLOC_GUARDS") != NULL)
152 {
153 guards = TRUE;
154 _dbus_verbose ("Will use dbus_malloc guards\n");
155 }
156
157 if (_dbus_getenv ("DBUS_DISABLE_MEM_POOLS") != NULL)
158 {
159 disable_mem_pools = TRUE;
160 _dbus_verbose ("Will disable memory pools\n");
161 }
162
163 if (_dbus_getenv ("DBUS_MALLOC_BACKTRACES") != NULL)
164 {
165 backtrace_on_fail_alloc = TRUE;
166 _dbus_verbose ("Will backtrace on failing a dbus_malloc\n");
167 }
168
169 if (_dbus_getenv ("DBUS_MALLOC_CANNOT_FAIL") != NULL)
170 {
171 malloc_cannot_fail = TRUE;
172 _dbus_verbose ("Will abort if system malloc() and friends fail\n");
173 }
174 }
175}
176
182dbus_bool_t
183_dbus_disable_mem_pools (void)
184{
185 _dbus_initialize_malloc_debug ();
186 return disable_mem_pools;
187}
188
197void
198_dbus_set_fail_alloc_counter (int until_next_fail)
199{
200 _dbus_initialize_malloc_debug ();
201
202 fail_alloc_counter = until_next_fail;
203
204#if 0
205 _dbus_verbose ("Set fail alloc counter = %d\n", fail_alloc_counter);
206#endif
207}
208
215int
216_dbus_get_fail_alloc_counter (void)
217{
218 _dbus_initialize_malloc_debug ();
219
220 return fail_alloc_counter;
221}
222
229void
230_dbus_set_fail_alloc_failures (int failures_per_failure)
231{
232 n_failures_per_failure = failures_per_failure;
233}
234
241int
242_dbus_get_fail_alloc_failures (void)
243{
244 return n_failures_per_failure;
245}
246
247#ifdef DBUS_ENABLE_EMBEDDED_TESTS
256dbus_bool_t
257_dbus_decrement_fail_alloc_counter (void)
258{
259 _dbus_initialize_malloc_debug ();
260
261 if (fail_alloc_counter <= 0)
262 {
263 if (backtrace_on_fail_alloc)
264 _dbus_print_backtrace ();
265
266 _dbus_verbose ("failure %d\n", n_failures_this_failure);
267
268 n_failures_this_failure += 1;
269 if (n_failures_this_failure >= n_failures_per_failure)
270 {
271 if (fail_nth >= 0)
272 fail_alloc_counter = fail_nth;
273 else
274 fail_alloc_counter = _DBUS_INT_MAX;
275
276 n_failures_this_failure = 0;
277
278 _dbus_verbose ("reset fail alloc counter to %d\n", fail_alloc_counter);
279 }
280
281 return TRUE;
282 }
283 else
284 {
285 fail_alloc_counter -= 1;
286 return FALSE;
287 }
288}
289#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
290
296int
297_dbus_get_malloc_blocks_outstanding (void)
298{
299 return _dbus_atomic_get (&n_blocks_outstanding);
300}
301
305typedef enum
306{
307 SOURCE_UNKNOWN,
308 SOURCE_MALLOC,
309 SOURCE_REALLOC,
310 SOURCE_MALLOC_ZERO,
311 SOURCE_REALLOC_NULL
312} BlockSource;
313
314static const char*
315source_string (BlockSource source)
316{
317 switch (source)
318 {
319 case SOURCE_UNKNOWN:
320 return "unknown";
321 case SOURCE_MALLOC:
322 return "malloc";
323 case SOURCE_REALLOC:
324 return "realloc";
325 case SOURCE_MALLOC_ZERO:
326 return "malloc0";
327 case SOURCE_REALLOC_NULL:
328 return "realloc(NULL)";
329 default:
330 _dbus_assert_not_reached ("Invalid malloc block source ID");
331 return "invalid!";
332 }
333}
334
335static void
336check_guards (void *free_block,
337 dbus_bool_t overwrite)
338{
339 if (free_block != NULL)
340 {
341 unsigned char *block = ((unsigned char*)free_block) - GUARD_START_OFFSET;
342 size_t requested_bytes = *(dbus_uint32_t *) (void *) block;
343 BlockSource source = *(dbus_uint32_t *) (void *) (block + 4);
344 unsigned int i;
345 dbus_bool_t failed;
346
347 failed = FALSE;
348
349#if 0
350 _dbus_verbose ("Checking %d bytes request from source %s\n",
351 requested_bytes, source_string (source));
352#endif
353
354 i = GUARD_INFO_SIZE;
355 while (i < GUARD_START_OFFSET)
356 {
357 dbus_uint32_t value = *(dbus_uint32_t *) (void *) &block[i];
358 if (value != GUARD_VALUE)
359 {
360 _dbus_warn ("Block of %lu bytes from %s had start guard value 0x%ux at %d expected 0x%x",
361 (long) requested_bytes, source_string (source),
362 value, i, GUARD_VALUE);
363 failed = TRUE;
364 }
365
366 i += 4;
367 }
368
369 i = GUARD_START_OFFSET + requested_bytes;
370 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
371 {
372 dbus_uint32_t value = *(dbus_uint32_t *) (void *) &block[i];
373 if (value != GUARD_VALUE)
374 {
375 _dbus_warn ("Block of %lu bytes from %s had end guard value 0x%ux at %d expected 0x%x",
376 (long) requested_bytes, source_string (source),
377 value, i, GUARD_VALUE);
378 failed = TRUE;
379 }
380
381 i += 4;
382 }
383
384 /* set memory to anything but nul bytes */
385 if (overwrite)
386 memset (free_block, 'g', requested_bytes);
387
388 if (failed)
389 _dbus_assert_not_reached ("guard value corruption");
390 }
391}
392
393static void*
394set_guards (void *real_block,
395 size_t requested_bytes,
396 BlockSource source)
397{
398 unsigned char *block = real_block;
399 unsigned int i;
400
401 if (block == NULL)
402 return NULL;
403
404 _dbus_assert (GUARD_START_OFFSET + GUARD_END_PAD == GUARD_EXTRA_SIZE);
405
406 *((dbus_uint32_t *) (void *) block) = requested_bytes;
407 *((dbus_uint32_t *) (void *) (block + 4)) = source;
408
409 i = GUARD_INFO_SIZE;
410 while (i < GUARD_START_OFFSET)
411 {
412 (*(dbus_uint32_t *) (void *) &block[i]) = GUARD_VALUE;
413
414 i += 4;
415 }
416
417 i = GUARD_START_OFFSET + requested_bytes;
418 while (i < (GUARD_START_OFFSET + requested_bytes + GUARD_END_PAD))
419 {
420 (*(dbus_uint32_t *) (void *) &block[i]) = GUARD_VALUE;
421
422 i += 4;
423 }
424
425 check_guards (block + GUARD_START_OFFSET, FALSE);
426
427 return block + GUARD_START_OFFSET;
428}
429
430#endif
431
/* End of internals docs */
433
434
453void*
454dbus_malloc (size_t bytes)
455{
456#ifdef DBUS_ENABLE_EMBEDDED_TESTS
457 _dbus_initialize_malloc_debug ();
458
459 if (_dbus_decrement_fail_alloc_counter ())
460 {
461 _dbus_verbose (" FAILING malloc of %ld bytes\n", (long) bytes);
462 return NULL;
463 }
464#endif
465
466 if (bytes == 0) /* some system mallocs handle this, some don't */
467 return NULL;
468#ifdef DBUS_ENABLE_EMBEDDED_TESTS
469 else if (fail_size != 0 && bytes > fail_size)
470 return NULL;
471 else if (guards)
472 {
473 void *block;
474
475 block = malloc (bytes + GUARD_EXTRA_SIZE);
476 if (block)
477 {
478 _dbus_atomic_inc (&n_blocks_outstanding);
479 }
480 else if (malloc_cannot_fail)
481 {
482 _dbus_warn ("out of memory: malloc (%ld + %ld)",
483 (long) bytes, (long) GUARD_EXTRA_SIZE);
484 _dbus_abort ();
485 }
486
487 return set_guards (block, bytes, SOURCE_MALLOC);
488 }
489#endif
490 else
491 {
492 void *mem;
493 mem = malloc (bytes);
494
495#ifdef DBUS_ENABLE_EMBEDDED_TESTS
496 if (mem)
497 {
498 _dbus_atomic_inc (&n_blocks_outstanding);
499 }
500 else if (malloc_cannot_fail)
501 {
502 _dbus_warn ("out of memory: malloc (%ld)", (long) bytes);
503 _dbus_abort ();
504 }
505#endif
506
507 return mem;
508 }
509}
510
523void*
524dbus_malloc0 (size_t bytes)
525{
526#ifdef DBUS_ENABLE_EMBEDDED_TESTS
527 _dbus_initialize_malloc_debug ();
528
529 if (_dbus_decrement_fail_alloc_counter ())
530 {
531 _dbus_verbose (" FAILING malloc0 of %ld bytes\n", (long) bytes);
532
533 return NULL;
534 }
535#endif
536
537 if (bytes == 0)
538 return NULL;
539#ifdef DBUS_ENABLE_EMBEDDED_TESTS
540 else if (fail_size != 0 && bytes > fail_size)
541 return NULL;
542 else if (guards)
543 {
544 void *block;
545
546 block = calloc (bytes + GUARD_EXTRA_SIZE, 1);
547
548 if (block)
549 {
550 _dbus_atomic_inc (&n_blocks_outstanding);
551 }
552 else if (malloc_cannot_fail)
553 {
554 _dbus_warn ("out of memory: calloc (%ld + %ld, 1)",
555 (long) bytes, (long) GUARD_EXTRA_SIZE);
556 _dbus_abort ();
557 }
558
559 return set_guards (block, bytes, SOURCE_MALLOC_ZERO);
560 }
561#endif
562 else
563 {
564 void *mem;
565 mem = calloc (bytes, 1);
566
567#ifdef DBUS_ENABLE_EMBEDDED_TESTS
568 if (mem)
569 {
570 _dbus_atomic_inc (&n_blocks_outstanding);
571 }
572 else if (malloc_cannot_fail)
573 {
574 _dbus_warn ("out of memory: calloc (%ld)", (long) bytes);
575 _dbus_abort ();
576 }
577#endif
578
579 return mem;
580 }
581}
582
593void*
594dbus_realloc (void *memory,
595 size_t bytes)
596{
597#ifdef DBUS_ENABLE_EMBEDDED_TESTS
598 _dbus_initialize_malloc_debug ();
599
600 if (_dbus_decrement_fail_alloc_counter ())
601 {
602 _dbus_verbose (" FAILING realloc of %ld bytes\n", (long) bytes);
603
604 return NULL;
605 }
606#endif
607
608 if (bytes == 0) /* guarantee this is safe */
609 {
610 dbus_free (memory);
611 return NULL;
612 }
613#ifdef DBUS_ENABLE_EMBEDDED_TESTS
614 else if (fail_size != 0 && bytes > fail_size)
615 return NULL;
616 else if (guards)
617 {
618 if (memory)
619 {
620 size_t old_bytes;
621 void *block;
622
623 check_guards (memory, FALSE);
624
625 block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET,
626 bytes + GUARD_EXTRA_SIZE);
627
628 if (block == NULL)
629 {
630 if (malloc_cannot_fail)
631 {
632 _dbus_warn ("out of memory: realloc (%p, %ld + %ld)",
633 memory, (long) bytes, (long) GUARD_EXTRA_SIZE);
634 _dbus_abort ();
635 }
636
637 return NULL;
638 }
639
640 old_bytes = *(dbus_uint32_t*)block;
641 if (bytes >= old_bytes)
642 /* old guards shouldn't have moved */
643 check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE);
644
645 return set_guards (block, bytes, SOURCE_REALLOC);
646 }
647 else
648 {
649 void *block;
650
651 block = malloc (bytes + GUARD_EXTRA_SIZE);
652
653 if (block)
654 {
655 _dbus_atomic_inc (&n_blocks_outstanding);
656 }
657 else if (malloc_cannot_fail)
658 {
659 _dbus_warn ("out of memory: malloc (%ld + %ld)",
660 (long) bytes, (long) GUARD_EXTRA_SIZE);
661 _dbus_abort ();
662 }
663
664 return set_guards (block, bytes, SOURCE_REALLOC_NULL);
665 }
666 }
667#endif
668 else
669 {
670 void *mem;
671 mem = realloc (memory, bytes);
672
673#ifdef DBUS_ENABLE_EMBEDDED_TESTS
674 if (mem == NULL && malloc_cannot_fail)
675 {
676 _dbus_warn ("out of memory: malloc (%ld)", (long) bytes);
677 _dbus_abort ();
678 }
679
680 if (memory == NULL && mem != NULL)
681 _dbus_atomic_inc (&n_blocks_outstanding);
682#endif
683 return mem;
684 }
685}
686
693void
694dbus_free (void *memory)
695{
696#ifdef DBUS_ENABLE_EMBEDDED_TESTS
697 if (guards)
698 {
699 check_guards (memory, TRUE);
700 if (memory)
701 {
702#ifdef DBUS_DISABLE_ASSERT
703 _dbus_atomic_dec (&n_blocks_outstanding);
704#else
705 dbus_int32_t old_value;
706
707 old_value = _dbus_atomic_dec (&n_blocks_outstanding);
708 _dbus_assert (old_value >= 1);
709#endif
710
711 free (((unsigned char*)memory) - GUARD_START_OFFSET);
712 }
713
714 return;
715 }
716#endif
717
718 if (memory) /* we guarantee it's safe to free (NULL) */
719 {
720#ifdef DBUS_ENABLE_EMBEDDED_TESTS
721#ifdef DBUS_DISABLE_ASSERT
722 _dbus_atomic_dec (&n_blocks_outstanding);
723#else
724 dbus_int32_t old_value;
725
726 old_value = _dbus_atomic_dec (&n_blocks_outstanding);
727 _dbus_assert (old_value >= 1);
728#endif
729#endif
730
731 free (memory);
732 }
733}
734
741void
742dbus_free_string_array (char **str_array)
743{
744 if (str_array)
745 {
746 int i;
747
748 i = 0;
749 while (str_array[i])
750 {
751 dbus_free (str_array[i]);
752 i++;
753 }
754
755 dbus_free (str_array);
756 }
757}
758
/* End of public API docs block */
760
761
775
780
785{
787 DBusShutdownFunction func;
788 void *data;
789};
790
791/* Protected by _DBUS_LOCK (shutdown_funcs) */
792static ShutdownClosure *registered_globals = NULL;
793
802dbus_bool_t
803_dbus_register_shutdown_func (DBusShutdownFunction func,
804 void *data)
805{
806 dbus_bool_t ok;
807
808 if (!_DBUS_LOCK (shutdown_funcs))
809 return FALSE;
810
811 ok = _dbus_register_shutdown_func_unlocked (func, data);
812 _DBUS_UNLOCK (shutdown_funcs);
813 return ok;
814}
815
816dbus_bool_t
817_dbus_register_shutdown_func_unlocked (DBusShutdownFunction func,
818 void *data)
819{
821
822 c = dbus_new (ShutdownClosure, 1);
823
824 if (c == NULL)
825 return FALSE;
826
827 c->func = func;
828 c->data = data;
829
830 c->next = registered_globals;
831 registered_globals = c;
832
833 return TRUE;
834}
835
/* End of private API docs block */
837
838
889void
891{
892 while (registered_globals != NULL)
893 {
895
896 c = registered_globals;
897 registered_globals = c->next;
898
899 (* c->func) (c->data);
900
901 dbus_free (c);
902 }
903
904 /* We wrap this in the thread-initialization lock because
905 * dbus_threads_init() uses the current generation to tell whether
906 * we're initialized, so we need to make sure that un-initializing
907 * propagates into all threads. */
911}
912
915#ifdef DBUS_ENABLE_EMBEDDED_TESTS
916#include "dbus-test.h"
917
923dbus_bool_t
924_dbus_memory_test (const char *test_data_dir _DBUS_GNUC_UNUSED)
925{
926 dbus_bool_t old_guards;
927 void *p;
928 size_t size;
929
930 old_guards = guards;
931 guards = TRUE;
932 p = dbus_malloc (4);
933 if (p == NULL)
934 _dbus_test_fatal ("no memory");
935 for (size = 4; size < 256; size += 4)
936 {
937 p = dbus_realloc (p, size);
938 if (p == NULL)
939 _dbus_test_fatal ("no memory");
940 }
941 for (size = 256; size != 0; size -= 4)
942 {
943 p = dbus_realloc (p, size);
944 if (p == NULL)
945 _dbus_test_fatal ("no memory");
946 }
947 dbus_free (p);
948 guards = old_guards;
949 return TRUE;
950}
951
952#endif
#define _dbus_assert_not_reached(explanation)
Aborts with an error message if called.
#define _dbus_assert(condition)
Aborts with an error message if the condition is false.
#define _DBUS_UNLOCK(name)
Unlocks a global lock.
#define _DBUS_LOCK(name)
Locks a global lock, initializing it first if necessary.
#define _DBUS_INT_MAX
Maximum value of type "int".
void _dbus_warn(const char *format,...)
Prints a warning message to stderr.
#define NULL
A null pointer, defined appropriately for C or C++.
Definition dbus-macros.h:51
#define TRUE
Expands to "1".
Definition dbus-macros.h:41
#define FALSE
Expands to "0".
Definition dbus-macros.h:44
int _dbus_current_generation
_dbus_current_generation is used to track each time that dbus_shutdown() is called,...
dbus_bool_t _dbus_register_shutdown_func(DBusShutdownFunction func, void *data)
Register a cleanup function to be called exactly once the next time dbus_shutdown() is called.
void dbus_shutdown(void)
Frees all memory allocated internally by libdbus and reverses the effects of dbus_threads_init().
void dbus_free(void *memory)
Frees a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
void * dbus_realloc(void *memory, size_t bytes)
Resizes a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
#define dbus_new(type, count)
Safe macro for using dbus_malloc().
Definition dbus-memory.h:59
void * dbus_malloc0(size_t bytes)
Allocates the given number of bytes, as with standard malloc(), but all bytes are initialized to zero...
void dbus_free_string_array(char **str_array)
Frees a NULL-terminated array of strings.
void * dbus_malloc(size_t bytes)
Allocates the given number of bytes, as with standard malloc().
dbus_int32_t _dbus_atomic_dec(DBusAtomic *atomic)
Atomically decrement an integer.
const char * _dbus_getenv(const char *varname)
Wrapper for getenv().
dbus_int32_t _dbus_atomic_get(DBusAtomic *atomic)
Atomically get the value of an integer.
void _dbus_threads_lock_platform_specific(void)
Lock a static mutex used to protect _dbus_threads_init_platform_specific().
void _dbus_threads_unlock_platform_specific(void)
Undo _dbus_threads_lock_platform_specific().
dbus_int32_t _dbus_atomic_inc(DBusAtomic *atomic)
Atomically increments an integer.
void _dbus_abort(void)
Aborts the program with SIGABRT (dumping core).
An atomic integer safe to increment or decrement from multiple threads.
This struct represents a function to be called on shutdown.
ShutdownClosure * next
Next ShutdownClosure.
DBusShutdownFunction func
Function to call.
void * data
Data for function.