1 /* Emulate Emacs heap dumping to test malloc_set_state.
2 Copyright (C) 2001-2017 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
24 #include <libc-symbols.h>
25 #include <shlib-compat.h>
29 /* Make the compatibility symbols availabile to this test case. */
30 void *malloc_get_state (void);
31 compat_symbol_reference (libc
, malloc_get_state
, malloc_get_state
, GLIBC_2_0
);
32 int malloc_set_state (void *);
33 compat_symbol_reference (libc
, malloc_set_state
, malloc_set_state
, GLIBC_2_0
);
35 static int do_test (void);
36 #define TEST_FUNCTION do_test ()
37 #include "../test-skeleton.c"
39 /* Maximum object size in the fake heap. */
40 enum { max_size
= 64 };
42 /* Allocation actions. These are randomized actions executed on the
43 dumped heap (see allocation_tasks below). They are interspersed
44 with operations on the new heap (see heap_activity). */
45 enum allocation_action
47 action_free
, /* Dumped and freed. */
48 action_realloc
, /* Dumped and realloc'ed. */
49 action_realloc_same
, /* Dumped and realloc'ed, same size. */
50 action_realloc_smaller
, /* Dumped and realloc'ed, shrinked. */
54 /* Dumped heap. Initialize it, so that the object is placed into the
55 .data section, for increased realism. The size is an upper bound;
56 we use about half of the space. */
57 static size_t dumped_heap
[action_count
* max_size
* max_size
58 / sizeof (size_t)] = {1};
60 /* Next free space in the dumped heap. Also top of the heap at the
61 end of the initialization procedure. */
62 static size_t *next_heap_chunk
;
64 /* Copied from malloc.c and hooks.c. The version is deliberately
65 lower than the final version of malloc_set_state. */
67 #define MALLOC_STATE_MAGIC 0x444c4541l
68 #define MALLOC_STATE_VERSION (0 * 0x100l + 4l)
73 void *av
[NBINS
* 2 + 2];
76 unsigned long trim_threshold
;
77 unsigned long top_pad
;
78 unsigned int n_mmaps_max
;
79 unsigned long mmap_threshold
;
81 unsigned long max_sbrked_mem
;
82 unsigned long max_total_mem
;
84 unsigned int max_n_mmaps
;
85 unsigned long mmapped_mem
;
86 unsigned long max_mmapped_mem
;
87 int using_malloc_checking
;
88 unsigned long max_fast
;
89 unsigned long arena_test
;
90 unsigned long arena_max
;
91 unsigned long narenas
;
94 .magic
= MALLOC_STATE_MAGIC
,
95 .version
= MALLOC_STATE_VERSION
,
98 /* Allocate a blob in the fake heap. */
100 dumped_heap_alloc (size_t length
)
102 /* malloc needs three state bits in the size field, so the minimum
103 alignment is 8 even on 32-bit architectures. malloc_set_state
104 should be compatible with such heaps even if it currently
105 provides more alignment to applications. */
109 heap_alignment_mask
= heap_alignment
- 1
111 _Static_assert (sizeof (size_t) <= heap_alignment
,
112 "size_t compatible with heap alignment");
114 /* Need at least this many bytes for metadata and application
116 size_t chunk_size
= sizeof (size_t) + length
;
117 /* Round up the allocation size to the heap alignment. */
118 chunk_size
+= heap_alignment_mask
;
119 chunk_size
&= ~heap_alignment_mask
;
120 if ((chunk_size
& 3) != 0)
122 /* The lower three bits in the chunk size have to be 0. */
123 write_message ("error: dumped_heap_alloc computed invalid chunk size\n");
126 if (next_heap_chunk
== NULL
)
127 /* Initialize the top of the heap. Add one word of zero padding,
128 to match existing practice. */
131 next_heap_chunk
= dumped_heap
+ 1;
134 /* The previous chunk is allocated. */
136 *next_heap_chunk
= chunk_size
;
138 /* User data starts after the chunk header. */
139 void *result
= next_heap_chunk
+ 1;
140 next_heap_chunk
+= chunk_size
/ sizeof (size_t);
142 /* Mark the previous chunk as used. */
143 *next_heap_chunk
= 1;
147 /* Global seed variable for the random number generator. */
148 static unsigned long long global_seed
;
150 /* Simple random number generator. The numbers are in the range from
151 0 to UINT_MAX (inclusive). */
153 rand_next (unsigned long long *seed
)
155 /* Linear congruential generated as used for MMIX. */
156 *seed
= *seed
* 6364136223846793005ULL + 1442695040888963407ULL;
160 /* Fill LENGTH bytes at BUFFER with random contents, as determined by
163 randomize_buffer (unsigned char *buffer
, size_t length
,
164 unsigned long long seed
)
166 for (size_t i
= 0; i
< length
; ++i
)
167 buffer
[i
] = rand_next (&seed
);
170 /* Dumps the buffer to standard output, in hexadecimal. */
172 dump_hex (unsigned char *buffer
, size_t length
)
174 for (int i
= 0; i
< length
; ++i
)
175 printf (" %02X", buffer
[i
]);
178 /* Set to true if an error is encountered. */
179 static bool errors
= false;
181 /* Keep track of object allocations. */
189 /* Check that the allocation task allocation has the expected
192 check_allocation (const struct allocation
*alloc
, int index
)
194 size_t size
= alloc
->size
;
195 if (alloc
->data
== NULL
)
197 printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n",
198 size
, index
, alloc
->seed
);
203 unsigned char expected
[4096];
204 if (size
> sizeof (expected
))
206 printf ("error: invalid allocation size %zu at %d, seed %u\n",
207 size
, index
, alloc
->seed
);
211 randomize_buffer (expected
, size
, alloc
->seed
);
212 if (memcmp (alloc
->data
, expected
, size
) != 0)
214 printf ("error: allocation %d data mismatch, size %zu, seed %u\n",
215 index
, size
, alloc
->seed
);
216 printf (" expected:");
217 dump_hex (expected
, size
);
220 dump_hex (alloc
->data
, size
);
226 /* A heap allocation combined with pending actions on it. */
227 struct allocation_task
229 struct allocation allocation
;
230 enum allocation_action action
;
233 /* Allocation tasks. Initialized by init_allocation_tasks and used by
234 perform_allocations. */
235 enum { allocation_task_count
= action_count
* max_size
};
236 static struct allocation_task allocation_tasks
[allocation_task_count
];
238 /* Fisher-Yates shuffle of allocation_tasks. */
240 shuffle_allocation_tasks (void)
242 for (int i
= 0; i
< allocation_task_count
- 1; ++i
)
244 /* Pick pair in the tail of the array. */
245 int j
= i
+ (rand_next (&global_seed
)
246 % ((unsigned) (allocation_task_count
- i
)));
247 if (j
< 0 || j
>= allocation_task_count
)
249 write_message ("error: test bug in shuffle\n");
253 struct allocation_task tmp
= allocation_tasks
[i
];
254 allocation_tasks
[i
] = allocation_tasks
[j
];
255 allocation_tasks
[j
] = tmp
;
259 /* Set up the allocation tasks and the dumped heap. */
261 initial_allocations (void)
263 /* Initialize in a position-dependent way. */
264 for (int i
= 0; i
< allocation_task_count
; ++i
)
265 allocation_tasks
[i
] = (struct allocation_task
)
269 .size
= 1 + (i
/ action_count
),
272 .action
= i
% action_count
275 /* Execute the tasks in a random order. */
276 shuffle_allocation_tasks ();
278 /* Initialize the contents of the dumped heap. */
279 for (int i
= 0; i
< allocation_task_count
; ++i
)
281 struct allocation_task
*task
= allocation_tasks
+ i
;
282 task
->allocation
.data
= dumped_heap_alloc (task
->allocation
.size
);
283 randomize_buffer (task
->allocation
.data
, task
->allocation
.size
,
284 task
->allocation
.seed
);
287 for (int i
= 0; i
< allocation_task_count
; ++i
)
288 check_allocation (&allocation_tasks
[i
].allocation
, i
);
291 /* Indicates whether init_heap has run. This variable needs to be
292 volatile because malloc is declared __THROW, which implies it is a
293 leaf function, but we expect it to run our hooks. */
294 static volatile bool heap_initialized
;
296 /* Executed by glibc malloc, through __malloc_initialize_hook
301 write_message ("info: performing heap initialization\n");
302 heap_initialized
= true;
304 /* Populate the dumped heap. */
305 initial_allocations ();
307 /* Complete initialization of the saved heap data structure. */
308 save_state
.sbrk_base
= (void *) dumped_heap
;
309 save_state
.sbrked_mem_bytes
= sizeof (dumped_heap
);
310 /* Top pointer. Adjust so that it points to the start of struct
312 save_state
.av
[2] = (void *) (next_heap_chunk
- 1);
314 /* Integrate the dumped heap into the process heap. */
315 if (malloc_set_state (&save_state
) != 0)
317 write_message ("error: malloc_set_state failed\n");
322 /* Interpose the initialization callback. */
323 void (*volatile __malloc_initialize_hook
) (void) = init_heap
;
325 /* Simulate occasional unrelated heap activity in the non-dumped
327 enum { heap_activity_allocations_count
= 32 };
328 static struct allocation heap_activity_allocations
329 [heap_activity_allocations_count
] = {};
330 static int heap_activity_seed_counter
= 1000 * 1000;
335 /* Only do this from time to time. */
336 if ((rand_next (&global_seed
) % 4) == 0)
338 int slot
= rand_next (&global_seed
) % heap_activity_allocations_count
;
339 struct allocation
*alloc
= heap_activity_allocations
+ slot
;
340 if (alloc
->data
== NULL
)
342 alloc
->size
= rand_next (&global_seed
) % (4096U + 1);
343 alloc
->data
= xmalloc (alloc
->size
);
344 alloc
->seed
= heap_activity_seed_counter
++;
345 randomize_buffer (alloc
->data
, alloc
->size
, alloc
->seed
);
346 check_allocation (alloc
, 1000 + slot
);
350 check_allocation (alloc
, 1000 + slot
);
358 heap_activity_deallocate (void)
360 for (int i
= 0; i
< heap_activity_allocations_count
; ++i
)
361 free (heap_activity_allocations
[i
].data
);
364 /* Perform a full heap check across the dumped heap allocation tasks,
365 and the simulated heap activity directly above. */
367 full_heap_check (void)
370 for (int i
= 0; i
< allocation_task_count
; ++i
)
371 if (allocation_tasks
[i
].allocation
.data
!= NULL
)
372 check_allocation (&allocation_tasks
[i
].allocation
, i
);
374 /* Heap activity allocations. */
375 for (int i
= 0; i
< heap_activity_allocations_count
; ++i
)
376 if (heap_activity_allocations
[i
].data
!= NULL
)
377 check_allocation (heap_activity_allocations
+ i
, i
);
380 /* Used as an optimization barrier to force a heap allocation. */
381 __attribute__ ((noinline
, noclone
))
391 my_free (malloc (1));
392 if (!heap_initialized
)
394 printf ("error: heap was not initialized by malloc\n");
398 /* The first pass performs the randomly generated allocation
400 write_message ("info: first pass through allocation tasks\n");
403 /* Execute the post-undump tasks in a random order. */
404 shuffle_allocation_tasks ();
406 for (int i
= 0; i
< allocation_task_count
; ++i
)
409 struct allocation_task
*task
= allocation_tasks
+ i
;
410 switch (task
->action
)
413 check_allocation (&task
->allocation
, i
);
414 free (task
->allocation
.data
);
415 task
->allocation
.data
= NULL
;
419 check_allocation (&task
->allocation
, i
);
420 task
->allocation
.data
= xrealloc
421 (task
->allocation
.data
, task
->allocation
.size
+ max_size
);
422 check_allocation (&task
->allocation
, i
);
425 case action_realloc_same
:
426 check_allocation (&task
->allocation
, i
);
427 task
->allocation
.data
= xrealloc
428 (task
->allocation
.data
, task
->allocation
.size
);
429 check_allocation (&task
->allocation
, i
);
432 case action_realloc_smaller
:
433 check_allocation (&task
->allocation
, i
);
434 size_t new_size
= task
->allocation
.size
- 1;
435 task
->allocation
.data
= xrealloc (task
->allocation
.data
, new_size
);
438 if (task
->allocation
.data
!= NULL
)
440 printf ("error: realloc with size zero did not deallocate\n");
443 /* No further action on this task. */
444 task
->action
= action_free
;
448 task
->allocation
.size
= new_size
;
449 check_allocation (&task
->allocation
, i
);
459 /* The second pass frees the objects which were allocated during the
461 write_message ("info: second pass through allocation tasks\n");
463 shuffle_allocation_tasks ();
464 for (int i
= 0; i
< allocation_task_count
; ++i
)
467 struct allocation_task
*task
= allocation_tasks
+ i
;
468 switch (task
->action
)
471 /* Already freed, nothing to do. */
475 case action_realloc_same
:
476 case action_realloc_smaller
:
477 check_allocation (&task
->allocation
, i
);
478 free (task
->allocation
.data
);
479 task
->allocation
.data
= NULL
;
488 heap_activity_deallocate ();
490 /* Check that the malloc_get_state stub behaves in the intended
493 if (malloc_get_state () != NULL
)
495 printf ("error: malloc_get_state succeeded\n");
500 printf ("error: malloc_get_state: %m\n");