mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-24 22:10:13 +00:00
malloc: Remove malloc_get_state, malloc_set_state [BZ #19473]
After the removal of __malloc_initialize_hook, newly compiled Emacs binaries are no longer able to use these interfaces. malloc_get_state is only used during the Emacs build process, so we provide a stub implementation only. Existing Emacs binaries will not call this stub function, but still reference the symbol. The rewritten tst-mallocstate test constructs a dumped heap which should approximates what existing Emacs binaries pass to glibc malloc.
This commit is contained in:
parent
261e6758e7
commit
e863cce57b
16
ChangeLog
16
ChangeLog
@ -1,3 +1,19 @@
|
|||||||
|
2016-10-26 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
[BZ #19473]
|
||||||
|
* malloc/malloc.h (malloc_get_state, malloc_set_state): Remove
|
||||||
|
declarations.
|
||||||
|
* malloc/malloc.c (malloc_get_state, malloc_set_state): Remove
|
||||||
|
weak aliases.
|
||||||
|
* malloc/hooks.c (__malloc_get_state): Remove definition.
|
||||||
|
(malloc_get_state): New stub implementation as
|
||||||
|
compatibility symbol.
|
||||||
|
(malloc_set_state): Rename from __malloc_set_state. Turn into
|
||||||
|
compat symbol.
|
||||||
|
* malloc/tst-mallocstate.c: Rewrite to approximate how Emacs uses
|
||||||
|
malloc_set_state.
|
||||||
|
* malloc/Makefile (LDFLAGS-tst-mallocstate): Link with -rdynamic.
|
||||||
|
|
||||||
2016-10-26 Florian Weimer <fweimer@redhat.com>
|
2016-10-26 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
* iconvdata/iso646.c (enum variant): Drop illegal_var.
|
* iconvdata/iso646.c (enum variant): Drop illegal_var.
|
||||||
|
8
NEWS
8
NEWS
@ -67,6 +67,14 @@ Version 2.25
|
|||||||
for the Linux quota interface which predates kernel version 2.4.22 has
|
for the Linux quota interface which predates kernel version 2.4.22 has
|
||||||
been removed.
|
been removed.
|
||||||
|
|
||||||
|
* The malloc_get_state and malloc_set_state functions have been removed.
|
||||||
|
Already-existing binaries that dynamically link to these functions will
|
||||||
|
get a hidden implementation in which malloc_get_state is a stub. As far
|
||||||
|
as we know, these functions are used only by GNU Emacs and this change
|
||||||
|
will not adversely affect already-built Emacs executables. Any undumped
|
||||||
|
Emacs executables, which normally exist only during an Emacs build, should
|
||||||
|
be rebuilt by re-running “./configure; make” in the Emacs build tree.
|
||||||
|
|
||||||
* The “ip6-dotint” and “no-ip6-dotint” resolver options, and the
|
* The “ip6-dotint” and “no-ip6-dotint” resolver options, and the
|
||||||
corresponding RES_NOIP6DOTINT flag from <resolv.h> have been removed.
|
corresponding RES_NOIP6DOTINT flag from <resolv.h> have been removed.
|
||||||
“no-ip6-dotint” had already been the default, and support for the
|
“no-ip6-dotint” had already been the default, and support for the
|
||||||
|
@ -69,6 +69,9 @@ $(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
|
|||||||
$(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
|
$(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
|
||||||
$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
|
$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
|
||||||
|
|
||||||
|
# Export the __malloc_initialize_hook variable to libc.so.
|
||||||
|
LDFLAGS-tst-mallocstate = -rdynamic
|
||||||
|
|
||||||
# These should be removed by `make clean'.
|
# These should be removed by `make clean'.
|
||||||
extra-objs = mcheck-init.o libmcheck.a
|
extra-objs = mcheck-init.o libmcheck.a
|
||||||
|
|
||||||
|
@ -447,6 +447,7 @@ memalign_check (size_t alignment, size_t bytes, const void *caller)
|
|||||||
return mem2mem_check (mem, bytes);
|
return mem2mem_check (mem, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
|
||||||
|
|
||||||
/* Get/set state: malloc_get_state() records the current state of all
|
/* Get/set state: malloc_get_state() records the current state of all
|
||||||
malloc variables (_except_ for the actual heap contents and `hook'
|
malloc variables (_except_ for the actual heap contents and `hook'
|
||||||
@ -492,60 +493,21 @@ struct malloc_save_state
|
|||||||
unsigned long narenas;
|
unsigned long narenas;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Dummy implementation which always fails. We need to provide this
|
||||||
|
symbol so that existing Emacs binaries continue to work with
|
||||||
|
BIND_NOW. */
|
||||||
void *
|
void *
|
||||||
__malloc_get_state (void)
|
attribute_compat_text_section
|
||||||
|
malloc_get_state (void)
|
||||||
{
|
{
|
||||||
struct malloc_save_state *ms;
|
__set_errno (ENOSYS);
|
||||||
int i;
|
return NULL;
|
||||||
mbinptr b;
|
|
||||||
|
|
||||||
ms = (struct malloc_save_state *) __libc_malloc (sizeof (*ms));
|
|
||||||
if (!ms)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
__libc_lock_lock (main_arena.mutex);
|
|
||||||
malloc_consolidate (&main_arena);
|
|
||||||
ms->magic = MALLOC_STATE_MAGIC;
|
|
||||||
ms->version = MALLOC_STATE_VERSION;
|
|
||||||
ms->av[0] = 0;
|
|
||||||
ms->av[1] = 0; /* used to be binblocks, now no longer used */
|
|
||||||
ms->av[2] = top (&main_arena);
|
|
||||||
ms->av[3] = 0; /* used to be undefined */
|
|
||||||
for (i = 1; i < NBINS; i++)
|
|
||||||
{
|
|
||||||
b = bin_at (&main_arena, i);
|
|
||||||
if (first (b) == b)
|
|
||||||
ms->av[2 * i + 2] = ms->av[2 * i + 3] = 0; /* empty bin */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ms->av[2 * i + 2] = first (b);
|
|
||||||
ms->av[2 * i + 3] = last (b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ms->sbrk_base = mp_.sbrk_base;
|
|
||||||
ms->sbrked_mem_bytes = main_arena.system_mem;
|
|
||||||
ms->trim_threshold = mp_.trim_threshold;
|
|
||||||
ms->top_pad = mp_.top_pad;
|
|
||||||
ms->n_mmaps_max = mp_.n_mmaps_max;
|
|
||||||
ms->mmap_threshold = mp_.mmap_threshold;
|
|
||||||
ms->check_action = check_action;
|
|
||||||
ms->max_sbrked_mem = main_arena.max_system_mem;
|
|
||||||
ms->max_total_mem = 0;
|
|
||||||
ms->n_mmaps = mp_.n_mmaps;
|
|
||||||
ms->max_n_mmaps = mp_.max_n_mmaps;
|
|
||||||
ms->mmapped_mem = mp_.mmapped_mem;
|
|
||||||
ms->max_mmapped_mem = mp_.max_mmapped_mem;
|
|
||||||
ms->using_malloc_checking = using_malloc_checking;
|
|
||||||
ms->max_fast = get_max_fast ();
|
|
||||||
ms->arena_test = mp_.arena_test;
|
|
||||||
ms->arena_max = mp_.arena_max;
|
|
||||||
ms->narenas = narenas;
|
|
||||||
__libc_lock_unlock (main_arena.mutex);
|
|
||||||
return (void *) ms;
|
|
||||||
}
|
}
|
||||||
|
compat_symbol (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
|
||||||
|
|
||||||
int
|
int
|
||||||
__malloc_set_state (void *msptr)
|
attribute_compat_text_section
|
||||||
|
malloc_set_state (void *msptr)
|
||||||
{
|
{
|
||||||
struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
|
struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
|
||||||
|
|
||||||
@ -612,6 +574,9 @@ __malloc_set_state (void *msptr)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
compat_symbol (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
|
||||||
|
|
||||||
|
#endif /* SHLIB_COMPAT */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Local variables:
|
* Local variables:
|
||||||
|
@ -5202,8 +5202,6 @@ strong_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
|
|||||||
weak_alias (__malloc_stats, malloc_stats)
|
weak_alias (__malloc_stats, malloc_stats)
|
||||||
weak_alias (__malloc_usable_size, malloc_usable_size)
|
weak_alias (__malloc_usable_size, malloc_usable_size)
|
||||||
weak_alias (__malloc_trim, malloc_trim)
|
weak_alias (__malloc_trim, malloc_trim)
|
||||||
weak_alias (__malloc_get_state, malloc_get_state)
|
|
||||||
weak_alias (__malloc_set_state, malloc_set_state)
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------
|
/* ------------------------------------------------------------
|
||||||
|
@ -134,13 +134,6 @@ extern void malloc_stats (void) __THROW;
|
|||||||
/* Output information about state of allocator to stream FP. */
|
/* Output information about state of allocator to stream FP. */
|
||||||
extern int malloc_info (int __options, FILE *__fp) __THROW;
|
extern int malloc_info (int __options, FILE *__fp) __THROW;
|
||||||
|
|
||||||
/* Record the state of all malloc variables in an opaque data structure. */
|
|
||||||
extern void *malloc_get_state (void) __THROW;
|
|
||||||
|
|
||||||
/* Restore the state of all malloc variables from data obtained with
|
|
||||||
malloc_get_state(). */
|
|
||||||
extern int malloc_set_state (void *__ptr) __THROW;
|
|
||||||
|
|
||||||
/* Hooks for debugging and user-defined versions. */
|
/* Hooks for debugging and user-defined versions. */
|
||||||
extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr,
|
extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr,
|
||||||
const void *)
|
const void *)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2001-2016 Free Software Foundation, Inc.
|
/* Emulate Emacs heap dumping to test malloc_set_state.
|
||||||
|
Copyright (C) 2001-2016 Free Software Foundation, Inc.
|
||||||
This file is part of the GNU C Library.
|
This file is part of the GNU C Library.
|
||||||
Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
|
Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
|
||||||
|
|
||||||
@ -17,68 +18,488 @@
|
|||||||
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libc-symbols.h>
|
||||||
|
#include <shlib-compat.h>
|
||||||
|
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
|
||||||
static int errors = 0;
|
/* Make the compatibility symbols availabile to this test case. */
|
||||||
|
void *malloc_get_state (void);
|
||||||
|
compat_symbol_reference (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
|
||||||
|
int malloc_set_state (void *);
|
||||||
|
compat_symbol_reference (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
|
||||||
|
|
||||||
|
static int do_test (void);
|
||||||
|
#define TEST_FUNCTION do_test ()
|
||||||
|
#include "../test-skeleton.c"
|
||||||
|
|
||||||
|
/* Maximum object size in the fake heap. */
|
||||||
|
enum { max_size = 64 };
|
||||||
|
|
||||||
|
/* Allocation actions. These are randomized actions executed on the
|
||||||
|
dumped heap (see allocation_tasks below). They are interspersed
|
||||||
|
with operations on the new heap (see heap_activity). */
|
||||||
|
enum allocation_action
|
||||||
|
{
|
||||||
|
action_free, /* Dumped and freed. */
|
||||||
|
action_realloc, /* Dumped and realloc'ed. */
|
||||||
|
action_realloc_same, /* Dumped and realloc'ed, same size. */
|
||||||
|
action_realloc_smaller, /* Dumped and realloc'ed, shrinked. */
|
||||||
|
action_count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Dumped heap. Initialize it, so that the object is placed into the
|
||||||
|
.data section, for increased realism. The size is an upper bound;
|
||||||
|
we use about half of the space. */
|
||||||
|
static size_t dumped_heap[action_count * max_size * max_size
|
||||||
|
/ sizeof (size_t)] = {1};
|
||||||
|
|
||||||
|
/* Next free space in the dumped heap. Also top of the heap at the
|
||||||
|
end of the initialization procedure. */
|
||||||
|
static size_t *next_heap_chunk;
|
||||||
|
|
||||||
|
/* Copied from malloc.c and hooks.c. The version is deliberately
|
||||||
|
lower than the final version of malloc_set_state. */
|
||||||
|
#define NBINS 128
|
||||||
|
#define MALLOC_STATE_MAGIC 0x444c4541l
|
||||||
|
#define MALLOC_STATE_VERSION (0 * 0x100l + 4l)
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
long magic;
|
||||||
|
long version;
|
||||||
|
void *av[NBINS * 2 + 2];
|
||||||
|
char *sbrk_base;
|
||||||
|
int sbrked_mem_bytes;
|
||||||
|
unsigned long trim_threshold;
|
||||||
|
unsigned long top_pad;
|
||||||
|
unsigned int n_mmaps_max;
|
||||||
|
unsigned long mmap_threshold;
|
||||||
|
int check_action;
|
||||||
|
unsigned long max_sbrked_mem;
|
||||||
|
unsigned long max_total_mem;
|
||||||
|
unsigned int n_mmaps;
|
||||||
|
unsigned int max_n_mmaps;
|
||||||
|
unsigned long mmapped_mem;
|
||||||
|
unsigned long max_mmapped_mem;
|
||||||
|
int using_malloc_checking;
|
||||||
|
unsigned long max_fast;
|
||||||
|
unsigned long arena_test;
|
||||||
|
unsigned long arena_max;
|
||||||
|
unsigned long narenas;
|
||||||
|
} save_state =
|
||||||
|
{
|
||||||
|
.magic = MALLOC_STATE_MAGIC,
|
||||||
|
.version = MALLOC_STATE_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Allocate a blob in the fake heap. */
|
||||||
|
static void *
|
||||||
|
dumped_heap_alloc (size_t length)
|
||||||
|
{
|
||||||
|
/* malloc needs three state bits in the size field, so the minimum
|
||||||
|
alignment is 8 even on 32-bit architectures. malloc_set_state
|
||||||
|
should be compatible with such heaps even if it currently
|
||||||
|
provides more alignment to applications. */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
heap_alignment = 8,
|
||||||
|
heap_alignment_mask = heap_alignment - 1
|
||||||
|
};
|
||||||
|
_Static_assert (sizeof (size_t) <= heap_alignment,
|
||||||
|
"size_t compatible with heap alignment");
|
||||||
|
|
||||||
|
/* Need at least this many bytes for metadata and application
|
||||||
|
data. */
|
||||||
|
size_t chunk_size = sizeof (size_t) + length;
|
||||||
|
/* Round up the allocation size to the heap alignment. */
|
||||||
|
chunk_size += heap_alignment_mask;
|
||||||
|
chunk_size &= ~heap_alignment_mask;
|
||||||
|
if ((chunk_size & 3) != 0)
|
||||||
|
{
|
||||||
|
/* The lower three bits in the chunk size have to be 0. */
|
||||||
|
write_message ("error: dumped_heap_alloc computed invalid chunk size\n");
|
||||||
|
_exit (1);
|
||||||
|
}
|
||||||
|
if (next_heap_chunk == NULL)
|
||||||
|
/* Initialize the top of the heap. Add one word of zero padding,
|
||||||
|
to match existing practice. */
|
||||||
|
{
|
||||||
|
dumped_heap[0] = 0;
|
||||||
|
next_heap_chunk = dumped_heap + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* The previous chunk is allocated. */
|
||||||
|
chunk_size |= 1;
|
||||||
|
*next_heap_chunk = chunk_size;
|
||||||
|
|
||||||
|
/* User data starts after the chunk header. */
|
||||||
|
void *result = next_heap_chunk + 1;
|
||||||
|
next_heap_chunk += chunk_size / sizeof (size_t);
|
||||||
|
|
||||||
|
/* Mark the previous chunk as used. */
|
||||||
|
*next_heap_chunk = 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global seed variable for the random number generator. */
|
||||||
|
static unsigned long long global_seed;
|
||||||
|
|
||||||
|
/* Simple random number generator. The numbers are in the range from
|
||||||
|
0 to UINT_MAX (inclusive). */
|
||||||
|
static unsigned int
|
||||||
|
rand_next (unsigned long long *seed)
|
||||||
|
{
|
||||||
|
/* Linear congruential generated as used for MMIX. */
|
||||||
|
*seed = *seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||||
|
return *seed >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill LENGTH bytes at BUFFER with random contents, as determined by
|
||||||
|
SEED. */
|
||||||
|
static void
|
||||||
|
randomize_buffer (unsigned char *buffer, size_t length,
|
||||||
|
unsigned long long seed)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
buffer[i] = rand_next (&seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dumps the buffer to standard output, in hexadecimal. */
|
||||||
|
static void
|
||||||
|
dump_hex (unsigned char *buffer, size_t length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
printf (" %02X", buffer[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set to true if an error is encountered. */
|
||||||
|
static bool errors = false;
|
||||||
|
|
||||||
|
/* Keep track of object allocations. */
|
||||||
|
struct allocation
|
||||||
|
{
|
||||||
|
unsigned char *data;
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int seed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check that the allocation task allocation has the expected
|
||||||
|
contents. */
|
||||||
|
static void
|
||||||
|
check_allocation (const struct allocation *alloc, int index)
|
||||||
|
{
|
||||||
|
size_t size = alloc->size;
|
||||||
|
if (alloc->data == NULL)
|
||||||
|
{
|
||||||
|
printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n",
|
||||||
|
size, index, alloc->seed);
|
||||||
|
errors = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char expected[4096];
|
||||||
|
if (size > sizeof (expected))
|
||||||
|
{
|
||||||
|
printf ("error: invalid allocation size %zu at %d, seed %u\n",
|
||||||
|
size, index, alloc->seed);
|
||||||
|
errors = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
randomize_buffer (expected, size, alloc->seed);
|
||||||
|
if (memcmp (alloc->data, expected, size) != 0)
|
||||||
|
{
|
||||||
|
printf ("error: allocation %d data mismatch, size %zu, seed %u\n",
|
||||||
|
index, size, alloc->seed);
|
||||||
|
printf (" expected:");
|
||||||
|
dump_hex (expected, size);
|
||||||
|
putc ('\n', stdout);
|
||||||
|
printf (" actual:");
|
||||||
|
dump_hex (alloc->data, size);
|
||||||
|
putc ('\n', stdout);
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A heap allocation combined with pending actions on it. */
|
||||||
|
struct allocation_task
|
||||||
|
{
|
||||||
|
struct allocation allocation;
|
||||||
|
enum allocation_action action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Allocation tasks. Initialized by init_allocation_tasks and used by
|
||||||
|
perform_allocations. */
|
||||||
|
enum { allocation_task_count = action_count * max_size };
|
||||||
|
static struct allocation_task allocation_tasks[allocation_task_count];
|
||||||
|
|
||||||
|
/* Fisher-Yates shuffle of allocation_tasks. */
|
||||||
|
static void
|
||||||
|
shuffle_allocation_tasks (void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < allocation_task_count - 1; ++i)
|
||||||
|
{
|
||||||
|
/* Pick pair in the tail of the array. */
|
||||||
|
int j = i + (rand_next (&global_seed)
|
||||||
|
% ((unsigned) (allocation_task_count - i)));
|
||||||
|
if (j < 0 || j >= allocation_task_count)
|
||||||
|
{
|
||||||
|
write_message ("error: test bug in shuffle\n");
|
||||||
|
_exit (1);
|
||||||
|
}
|
||||||
|
/* Exchange. */
|
||||||
|
struct allocation_task tmp = allocation_tasks[i];
|
||||||
|
allocation_tasks[i] = allocation_tasks[j];
|
||||||
|
allocation_tasks[j] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the allocation tasks and the dumped heap. */
|
||||||
|
static void
|
||||||
|
initial_allocations (void)
|
||||||
|
{
|
||||||
|
/* Initialize in a position-dependent way. */
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
allocation_tasks[i] = (struct allocation_task)
|
||||||
|
{
|
||||||
|
.allocation =
|
||||||
|
{
|
||||||
|
.size = 1 + (i / action_count),
|
||||||
|
.seed = i,
|
||||||
|
},
|
||||||
|
.action = i % action_count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Execute the tasks in a random order. */
|
||||||
|
shuffle_allocation_tasks ();
|
||||||
|
|
||||||
|
/* Initialize the contents of the dumped heap. */
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
{
|
||||||
|
struct allocation_task *task = allocation_tasks + i;
|
||||||
|
task->allocation.data = dumped_heap_alloc (task->allocation.size);
|
||||||
|
randomize_buffer (task->allocation.data, task->allocation.size,
|
||||||
|
task->allocation.seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
check_allocation (&allocation_tasks[i].allocation, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indicates whether init_heap has run. This variable needs to be
|
||||||
|
volatile because malloc is declared __THROW, which implies it is a
|
||||||
|
leaf function, but we expect it to run our hooks. */
|
||||||
|
static volatile bool heap_initialized;
|
||||||
|
|
||||||
|
/* Executed by glibc malloc, through __malloc_initialize_hook
|
||||||
|
below. */
|
||||||
|
static void
|
||||||
|
init_heap (void)
|
||||||
|
{
|
||||||
|
write_message ("info: performing heap initialization\n");
|
||||||
|
heap_initialized = true;
|
||||||
|
|
||||||
|
/* Populate the dumped heap. */
|
||||||
|
initial_allocations ();
|
||||||
|
|
||||||
|
/* Complete initialization of the saved heap data structure. */
|
||||||
|
save_state.sbrk_base = (void *) dumped_heap;
|
||||||
|
save_state.sbrked_mem_bytes = sizeof (dumped_heap);
|
||||||
|
/* Top pointer. Adjust so that it points to the start of struct
|
||||||
|
malloc_chunk. */
|
||||||
|
save_state.av[2] = (void *) (next_heap_chunk - 1);
|
||||||
|
|
||||||
|
/* Integrate the dumped heap into the process heap. */
|
||||||
|
if (malloc_set_state (&save_state) != 0)
|
||||||
|
{
|
||||||
|
write_message ("error: malloc_set_state failed\n");
|
||||||
|
_exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interpose the initialization callback. */
|
||||||
|
void (*volatile __malloc_initialize_hook) (void) = init_heap;
|
||||||
|
|
||||||
|
/* Simulate occasional unrelated heap activity in the non-dumped
|
||||||
|
heap. */
|
||||||
|
enum { heap_activity_allocations_count = 32 };
|
||||||
|
static struct allocation heap_activity_allocations
|
||||||
|
[heap_activity_allocations_count] = {};
|
||||||
|
static int heap_activity_seed_counter = 1000 * 1000;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
merror (const char *msg)
|
heap_activity (void)
|
||||||
{
|
{
|
||||||
++errors;
|
/* Only do this from time to time. */
|
||||||
printf ("Error: %s\n", msg);
|
if ((rand_next (&global_seed) % 4) == 0)
|
||||||
|
{
|
||||||
|
int slot = rand_next (&global_seed) % heap_activity_allocations_count;
|
||||||
|
struct allocation *alloc = heap_activity_allocations + slot;
|
||||||
|
if (alloc->data == NULL)
|
||||||
|
{
|
||||||
|
alloc->size = rand_next (&global_seed) % (4096U + 1);
|
||||||
|
alloc->data = xmalloc (alloc->size);
|
||||||
|
alloc->seed = heap_activity_seed_counter++;
|
||||||
|
randomize_buffer (alloc->data, alloc->size, alloc->seed);
|
||||||
|
check_allocation (alloc, 1000 + slot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
check_allocation (alloc, 1000 + slot);
|
||||||
|
free (alloc->data);
|
||||||
|
alloc->data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
heap_activity_deallocate (void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < heap_activity_allocations_count; ++i)
|
||||||
|
free (heap_activity_allocations[i].data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform a full heap check across the dumped heap allocation tasks,
|
||||||
|
and the simulated heap activity directly above. */
|
||||||
|
static void
|
||||||
|
full_heap_check (void)
|
||||||
|
{
|
||||||
|
/* Dumped heap. */
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
if (allocation_tasks[i].allocation.data != NULL)
|
||||||
|
check_allocation (&allocation_tasks[i].allocation, i);
|
||||||
|
|
||||||
|
/* Heap activity allocations. */
|
||||||
|
for (int i = 0; i < heap_activity_allocations_count; ++i)
|
||||||
|
if (heap_activity_allocations[i].data != NULL)
|
||||||
|
check_allocation (heap_activity_allocations + i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used as an optimization barrier to force a heap allocation. */
|
||||||
|
__attribute__ ((noinline, noclone))
|
||||||
|
static void
|
||||||
|
my_free (void *ptr)
|
||||||
|
{
|
||||||
|
free (ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_test (void)
|
do_test (void)
|
||||||
{
|
{
|
||||||
void *p1, *p2;
|
my_free (malloc (1));
|
||||||
void *save_state;
|
if (!heap_initialized)
|
||||||
long i;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
p1 = malloc (10);
|
|
||||||
if (p1 == NULL)
|
|
||||||
merror ("malloc (10) failed.");
|
|
||||||
|
|
||||||
p2 = malloc (20);
|
|
||||||
if (p2 == NULL)
|
|
||||||
merror ("malloc (20) failed.");
|
|
||||||
|
|
||||||
free (malloc (10));
|
|
||||||
|
|
||||||
for (i = 0; i < 100; ++i)
|
|
||||||
{
|
{
|
||||||
save_state = malloc_get_state ();
|
printf ("error: heap was not initialized by malloc\n");
|
||||||
if (save_state == NULL)
|
return 1;
|
||||||
{
|
|
||||||
merror ("malloc_get_state () failed.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*free (malloc (10)); This could change the top chunk! */
|
|
||||||
malloc_set_state (save_state);
|
|
||||||
p1 = realloc (p1, i * 4 + 4);
|
|
||||||
if (p1 == NULL)
|
|
||||||
merror ("realloc (i*4) failed.");
|
|
||||||
free (save_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 = realloc (p1, 40);
|
/* The first pass performs the randomly generated allocation
|
||||||
free (p2);
|
tasks. */
|
||||||
p2 = malloc (10);
|
write_message ("info: first pass through allocation tasks\n");
|
||||||
if (p2 == NULL)
|
full_heap_check ();
|
||||||
merror ("malloc (10) failed.");
|
|
||||||
free (p1);
|
|
||||||
|
|
||||||
return errors != 0;
|
/* Execute the post-undump tasks in a random order. */
|
||||||
|
shuffle_allocation_tasks ();
|
||||||
|
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
{
|
||||||
|
heap_activity ();
|
||||||
|
struct allocation_task *task = allocation_tasks + i;
|
||||||
|
switch (task->action)
|
||||||
|
{
|
||||||
|
case action_free:
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
free (task->allocation.data);
|
||||||
|
task->allocation.data = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_realloc:
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
task->allocation.data = xrealloc
|
||||||
|
(task->allocation.data, task->allocation.size + max_size);
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_realloc_same:
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
task->allocation.data = xrealloc
|
||||||
|
(task->allocation.data, task->allocation.size);
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_realloc_smaller:
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
size_t new_size = task->allocation.size - 1;
|
||||||
|
task->allocation.data = xrealloc (task->allocation.data, new_size);
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
if (task->allocation.data != NULL)
|
||||||
|
{
|
||||||
|
printf ("error: realloc with size zero did not deallocate\n");
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
/* No further action on this task. */
|
||||||
|
task->action = action_free;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task->allocation.size = new_size;
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_count:
|
||||||
|
abort ();
|
||||||
|
}
|
||||||
|
full_heap_check ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The second pass frees the objects which were allocated during the
|
||||||
|
first pass. */
|
||||||
|
write_message ("info: second pass through allocation tasks\n");
|
||||||
|
|
||||||
|
shuffle_allocation_tasks ();
|
||||||
|
for (int i = 0; i < allocation_task_count; ++i)
|
||||||
|
{
|
||||||
|
heap_activity ();
|
||||||
|
struct allocation_task *task = allocation_tasks + i;
|
||||||
|
switch (task->action)
|
||||||
|
{
|
||||||
|
case action_free:
|
||||||
|
/* Already freed, nothing to do. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_realloc:
|
||||||
|
case action_realloc_same:
|
||||||
|
case action_realloc_smaller:
|
||||||
|
check_allocation (&task->allocation, i);
|
||||||
|
free (task->allocation.data);
|
||||||
|
task->allocation.data = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case action_count:
|
||||||
|
abort ();
|
||||||
|
}
|
||||||
|
full_heap_check ();
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_activity_deallocate ();
|
||||||
|
|
||||||
|
/* Check that the malloc_get_state stub behaves in the intended
|
||||||
|
way. */
|
||||||
|
errno = 0;
|
||||||
|
if (malloc_get_state () != NULL)
|
||||||
|
{
|
||||||
|
printf ("error: malloc_get_state succeeded\n");
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
if (errno != ENOSYS)
|
||||||
|
{
|
||||||
|
printf ("error: malloc_get_state: %m\n");
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Local variables:
|
|
||||||
* c-basic-offset: 2
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define TEST_FUNCTION do_test ()
|
|
||||||
#include "../test-skeleton.c"
|
|
||||||
|
Loading…
Reference in New Issue
Block a user