1
2 /*--------------------------------------------------------------------*/
3 /*--- malloc/free wrappers for detecting errors and updating bits. ---*/
4 /*--- mc_malloc_wrappers.c ---*/
5 /*--------------------------------------------------------------------*/
6
7 /*
8 This file is part of MemCheck, a heavyweight Valgrind tool for
9 detecting memory errors.
10
11 Copyright (C) 2000-2011 Julian Seward
12 jseward@acm.org
13
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
29 The GNU General Public License is contained in the file COPYING.
30 */
31
32 #include "pub_tool_basics.h"
33 #include "pub_tool_execontext.h"
34 #include "pub_tool_hashtable.h"
35 #include "pub_tool_libcbase.h"
36 #include "pub_tool_libcassert.h"
37 #include "pub_tool_libcprint.h"
38 #include "pub_tool_mallocfree.h"
39 #include "pub_tool_options.h"
40 #include "pub_tool_replacemalloc.h"
41 #include "pub_tool_threadstate.h"
42 #include "pub_tool_tooliface.h" // Needed for mc_include.h
43 #include "pub_tool_stacktrace.h" // For VG_(get_and_pp_StackTrace)
44
45 #include "mc_include.h"
46
47 /*------------------------------------------------------------*/
48 /*--- Defns ---*/
49 /*------------------------------------------------------------*/
50
51 /* Stats ... */
52 static SizeT cmalloc_n_mallocs = 0;
53 static SizeT cmalloc_n_frees = 0;
54 static ULong cmalloc_bs_mallocd = 0;
55
56 /* For debug printing to do with mempools: what stack trace
57 depth to show. */
58 #define MEMPOOL_DEBUG_STACKTRACE_DEPTH 16
59
60
61 /*------------------------------------------------------------*/
62 /*--- Tracking malloc'd and free'd blocks ---*/
63 /*------------------------------------------------------------*/
64
65 /* Record malloc'd blocks. */
66 VgHashTable MC_(malloc_list) = NULL;
67
68 /* Memory pools: a hash table of MC_Mempools. Search key is
69 MC_Mempool::pool. */
70 VgHashTable MC_(mempool_list) = NULL;
71
72 /* Records blocks after freeing. */
73 /* Blocks freed by the client are queued in one of two lists of
74 freed blocks not yet physically freed:
75 "big blocks" freed list.
76 "small blocks" freed list
77 The blocks with a size >= MC_(clo_freelist_big_blocks)
78 are linked in the big blocks freed list.
79 This allows a client to allocate and free big blocks
80 (e.g. bigger than VG_(clo_freelist_vol)) without losing
81 immediately all protection against dangling pointers.
82 position [0] is for big blocks, [1] is for small blocks. */
83 static MC_Chunk* freed_list_start[2] = {NULL, NULL};
84 static MC_Chunk* freed_list_end[2] = {NULL, NULL};
85
86 /* Put a shadow chunk on the freed blocks queue, possibly freeing up
87 some of the oldest blocks in the queue at the same time. */
add_to_freed_queue(MC_Chunk * mc)88 static void add_to_freed_queue ( MC_Chunk* mc )
89 {
90 const Bool show = False;
91 const int l = (mc->szB >= MC_(clo_freelist_big_blocks) ? 0 : 1);
92
93 /* Put it at the end of the freed list, unless the block
94 would be directly released any way : in this case, we
95 put it at the head of the freed list. */
96 if (freed_list_end[l] == NULL) {
97 tl_assert(freed_list_start[l] == NULL);
98 mc->next = NULL;
99 freed_list_end[l] = freed_list_start[l] = mc;
100 } else {
101 tl_assert(freed_list_end[l]->next == NULL);
102 if (mc->szB >= MC_(clo_freelist_vol)) {
103 mc->next = freed_list_start[l];
104 freed_list_start[l] = mc;
105 } else {
106 mc->next = NULL;
107 freed_list_end[l]->next = mc;
108 freed_list_end[l] = mc;
109 }
110 }
111 VG_(free_queue_volume) += (Long)mc->szB;
112 if (show)
113 VG_(printf)("mc_freelist: acquire: volume now %lld\n",
114 VG_(free_queue_volume));
115 VG_(free_queue_length)++;
116 }
117
118 /* Release enough of the oldest blocks to bring the free queue
119 volume below vg_clo_freelist_vol.
120 Start with big block list first.
121 On entry, VG_(free_queue_volume) must be > MC_(clo_freelist_vol).
122 On exit, VG_(free_queue_volume) will be <= MC_(clo_freelist_vol). */
release_oldest_block(void)123 static void release_oldest_block(void)
124 {
125 const Bool show = False;
126 int i;
127 tl_assert (VG_(free_queue_volume) > MC_(clo_freelist_vol));
128 tl_assert (freed_list_start[0] != NULL || freed_list_start[1] != NULL);
129
130 for (i = 0; i < 2; i++) {
131 while (VG_(free_queue_volume) > MC_(clo_freelist_vol)
132 && freed_list_start[i] != NULL) {
133 MC_Chunk* mc1;
134 tl_assert(freed_list_end[i] != NULL);
135
136 mc1 = freed_list_start[i];
137 VG_(free_queue_volume) -= (Long)mc1->szB;
138 VG_(free_queue_length)--;
139 if (show)
140 VG_(printf)("mc_freelist: discard: volume now %lld\n",
141 VG_(free_queue_volume));
142 tl_assert(VG_(free_queue_volume) >= 0);
143
144 if (freed_list_start[i] == freed_list_end[i]) {
145 freed_list_start[i] = freed_list_end[i] = NULL;
146 } else {
147 freed_list_start[i] = mc1->next;
148 }
149 mc1->next = NULL; /* just paranoia */
150 /* free MC_Chunk */
151 if (MC_AllocCustom != mc1->allockind)
152 VG_(cli_free) ( (void*)(mc1->data) );
153 VG_(free) ( mc1 );
154 }
155 }
156 }
157
MC_(get_freed_block_bracketting)158 MC_Chunk* MC_(get_freed_block_bracketting) (Addr a)
159 {
160 int i;
161 for (i = 0; i < 2; i++) {
162 MC_Chunk* mc;
163 mc = freed_list_start[i];
164 while (mc) {
165 if (VG_(addr_is_in_block)( a, mc->data, mc->szB,
166 MC_MALLOC_REDZONE_SZB ))
167 return mc;
168 mc = mc->next;
169 }
170 }
171 return NULL;
172 }
173
174 /* Allocate a shadow chunk, put it on the appropriate list.
175 If needed, release oldest blocks from freed list. */
176 static
create_MC_Chunk(ExeContext * ec,Addr p,SizeT szB,MC_AllocKind kind)177 MC_Chunk* create_MC_Chunk ( ExeContext* ec, Addr p, SizeT szB,
178 MC_AllocKind kind)
179 {
180 MC_Chunk* mc = VG_(malloc)("mc.cMC.1 (a MC_Chunk)", sizeof(MC_Chunk));
181 mc->data = p;
182 mc->szB = szB;
183 mc->allockind = kind;
184 mc->where = ec;
185
186 /* Each time a new MC_Chunk is created, release oldest blocks
187 if the free list volume is exceeded. */
188 if (VG_(free_queue_volume) > MC_(clo_freelist_vol))
189 release_oldest_block();
190
191 /* Paranoia ... ensure the MC_Chunk is off-limits to the client, so
192 the mc->data field isn't visible to the leak checker. If memory
193 management is working correctly, any pointer returned by VG_(malloc)
194 should be noaccess as far as the client is concerned. */
195 if (!MC_(check_mem_is_noaccess)( (Addr)mc, sizeof(MC_Chunk), NULL )) {
196 VG_(tool_panic)("create_MC_Chunk: shadow area is accessible");
197 }
198 return mc;
199 }
200
201 /*------------------------------------------------------------*/
202 /*--- client_malloc(), etc ---*/
203 /*------------------------------------------------------------*/
204
205 // XXX: should make this a proper error (bug #79311).
complain_about_silly_args(SizeT sizeB,Char * fn)206 static Bool complain_about_silly_args(SizeT sizeB, Char* fn)
207 {
208 // Cast to a signed type to catch any unexpectedly negative args. We're
209 // assuming here that the size asked for is not greater than 2^31 bytes
210 // (for 32-bit platforms) or 2^63 bytes (for 64-bit platforms).
211 if ((SSizeT)sizeB < 0) {
212 if (!VG_(clo_xml))
213 VG_(message)(Vg_UserMsg, "Warning: silly arg (%ld) to %s()\n",
214 (SSizeT)sizeB, fn );
215 return True;
216 }
217 return False;
218 }
219
complain_about_silly_args2(SizeT n,SizeT sizeB)220 static Bool complain_about_silly_args2(SizeT n, SizeT sizeB)
221 {
222 if ((SSizeT)n < 0 || (SSizeT)sizeB < 0) {
223 if (!VG_(clo_xml))
224 VG_(message)(Vg_UserMsg,
225 "Warning: silly args (%ld,%ld) to calloc()\n",
226 (SSizeT)n, (SSizeT)sizeB);
227 return True;
228 }
229 return False;
230 }
231
232 /* Allocate memory and note change in memory available */
MC_(new_block)233 void* MC_(new_block) ( ThreadId tid,
234 Addr p, SizeT szB, SizeT alignB,
235 Bool is_zeroed, MC_AllocKind kind, VgHashTable table)
236 {
237 ExeContext* ec;
238
239 cmalloc_n_mallocs ++;
240
241 // Allocate and zero if necessary
242 if (p) {
243 tl_assert(MC_AllocCustom == kind);
244 } else {
245 tl_assert(MC_AllocCustom != kind);
246 p = (Addr)VG_(cli_malloc)( alignB, szB );
247 if (!p) {
248 return NULL;
249 }
250 if (is_zeroed) {
251 VG_(memset)((void*)p, 0, szB);
252 } else
253 if (MC_(clo_malloc_fill) != -1) {
254 tl_assert(MC_(clo_malloc_fill) >= 0x00 && MC_(clo_malloc_fill) <= 0xFF);
255 VG_(memset)((void*)p, MC_(clo_malloc_fill), szB);
256 }
257 }
258
259 // Only update this stat if allocation succeeded.
260 cmalloc_bs_mallocd += (ULong)szB;
261
262 ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
263 tl_assert(ec);
264
265 VG_(HT_add_node)( table, create_MC_Chunk(ec, p, szB, kind) );
266
267 if (is_zeroed)
268 MC_(make_mem_defined)( p, szB );
269 else {
270 UInt ecu = VG_(get_ECU_from_ExeContext)(ec);
271 tl_assert(VG_(is_plausible_ECU)(ecu));
272 MC_(make_mem_undefined_w_otag)( p, szB, ecu | MC_OKIND_HEAP );
273 }
274
275 return (void*)p;
276 }
277
MC_(malloc)278 void* MC_(malloc) ( ThreadId tid, SizeT n )
279 {
280 if (complain_about_silly_args(n, "malloc")) {
281 return NULL;
282 } else {
283 return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
284 /*is_zeroed*/False, MC_AllocMalloc, MC_(malloc_list));
285 }
286 }
287
MC_(__builtin_new)288 void* MC_(__builtin_new) ( ThreadId tid, SizeT n )
289 {
290 if (complain_about_silly_args(n, "__builtin_new")) {
291 return NULL;
292 } else {
293 return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
294 /*is_zeroed*/False, MC_AllocNew, MC_(malloc_list));
295 }
296 }
297
MC_(__builtin_vec_new)298 void* MC_(__builtin_vec_new) ( ThreadId tid, SizeT n )
299 {
300 if (complain_about_silly_args(n, "__builtin_vec_new")) {
301 return NULL;
302 } else {
303 return MC_(new_block) ( tid, 0, n, VG_(clo_alignment),
304 /*is_zeroed*/False, MC_AllocNewVec, MC_(malloc_list));
305 }
306 }
307
MC_(memalign)308 void* MC_(memalign) ( ThreadId tid, SizeT alignB, SizeT n )
309 {
310 if (complain_about_silly_args(n, "memalign")) {
311 return NULL;
312 } else {
313 return MC_(new_block) ( tid, 0, n, alignB,
314 /*is_zeroed*/False, MC_AllocMalloc, MC_(malloc_list));
315 }
316 }
317
MC_(calloc)318 void* MC_(calloc) ( ThreadId tid, SizeT nmemb, SizeT size1 )
319 {
320 if (complain_about_silly_args2(nmemb, size1)) {
321 return NULL;
322 } else {
323 return MC_(new_block) ( tid, 0, nmemb*size1, VG_(clo_alignment),
324 /*is_zeroed*/True, MC_AllocMalloc, MC_(malloc_list));
325 }
326 }
327
328 static
die_and_free_mem(ThreadId tid,MC_Chunk * mc,SizeT rzB)329 void die_and_free_mem ( ThreadId tid, MC_Chunk* mc, SizeT rzB )
330 {
331 if (MC_(clo_free_fill) != -1) {
332 tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
333 VG_(memset)((void*)mc->data, MC_(clo_free_fill), mc->szB);
334 }
335
336 /* Note: make redzones noaccess again -- just in case user made them
337 accessible with a client request... */
338 MC_(make_mem_noaccess)( mc->data-rzB, mc->szB + 2*rzB );
339
340 /* Record where freed */
341 mc->where = VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ );
342 /* Put it out of harm's way for a while */
343 add_to_freed_queue ( mc );
344 /* If the free list volume is bigger than MC_(clo_freelist_vol),
345 we wait till the next block allocation to release blocks.
346 This increase the chance to discover dangling pointer usage,
347 even for big blocks being freed by the client. */
348 }
349
MC_(handle_free)350 void MC_(handle_free) ( ThreadId tid, Addr p, UInt rzB, MC_AllocKind kind )
351 {
352 MC_Chunk* mc;
353
354 cmalloc_n_frees++;
355
356 mc = VG_(HT_remove) ( MC_(malloc_list), (UWord)p );
357 if (mc == NULL) {
358 MC_(record_free_error) ( tid, p );
359 } else {
360 /* check if it is a matching free() / delete / delete [] */
361 if (kind != mc->allockind) {
362 tl_assert(p == mc->data);
363 MC_(record_freemismatch_error) ( tid, mc );
364 }
365 die_and_free_mem ( tid, mc, rzB );
366 }
367 }
368
MC_(free)369 void MC_(free) ( ThreadId tid, void* p )
370 {
371 MC_(handle_free)(
372 tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocMalloc );
373 }
374
MC_(__builtin_delete)375 void MC_(__builtin_delete) ( ThreadId tid, void* p )
376 {
377 MC_(handle_free)(
378 tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocNew);
379 }
380
MC_(__builtin_vec_delete)381 void MC_(__builtin_vec_delete) ( ThreadId tid, void* p )
382 {
383 MC_(handle_free)(
384 tid, (Addr)p, MC_MALLOC_REDZONE_SZB, MC_AllocNewVec);
385 }
386
MC_(realloc)387 void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB )
388 {
389 MC_Chunk* mc;
390 void* p_new;
391 SizeT old_szB;
392
393 cmalloc_n_frees ++;
394 cmalloc_n_mallocs ++;
395 cmalloc_bs_mallocd += (ULong)new_szB;
396
397 if (complain_about_silly_args(new_szB, "realloc"))
398 return NULL;
399
400 /* Remove the old block */
401 mc = VG_(HT_remove) ( MC_(malloc_list), (UWord)p_old );
402 if (mc == NULL) {
403 MC_(record_free_error) ( tid, (Addr)p_old );
404 /* We return to the program regardless. */
405 return NULL;
406 }
407
408 /* check if its a matching free() / delete / delete [] */
409 if (MC_AllocMalloc != mc->allockind) {
410 /* can not realloc a range that was allocated with new or new [] */
411 tl_assert((Addr)p_old == mc->data);
412 MC_(record_freemismatch_error) ( tid, mc );
413 /* but keep going anyway */
414 }
415
416 old_szB = mc->szB;
417
418 /* In all cases, even when the new size is smaller or unchanged, we
419 reallocate and copy the contents, and make the old block
420 inaccessible. This is so as to guarantee to catch all cases of
421 accesses via the old address after reallocation, regardless of
422 the change in size. (Of course the ability to detect accesses
423 to the old block also depends on the size of the freed blocks
424 queue). */
425
426 if (new_szB <= old_szB) {
427 /* new size is smaller or the same */
428 Addr a_new;
429 /* Get new memory */
430 a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
431
432 if (a_new) {
433 ExeContext* ec;
434
435 ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
436 tl_assert(ec);
437
438 /* Retained part is copied, red zones set as normal */
439 MC_(make_mem_noaccess)( a_new-MC_MALLOC_REDZONE_SZB,
440 MC_MALLOC_REDZONE_SZB );
441 MC_(copy_address_range_state) ( (Addr)p_old, a_new, new_szB );
442 MC_(make_mem_noaccess) ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
443
444 /* Copy from old to new */
445 VG_(memcpy)((void*)a_new, p_old, new_szB);
446
447 /* Possibly fill freed area with specified junk. */
448 if (MC_(clo_free_fill) != -1) {
449 tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
450 VG_(memset)((void*)p_old, MC_(clo_free_fill), old_szB);
451 }
452
453 /* Free old memory */
454 /* Nb: we have to allocate a new MC_Chunk for the new memory rather
455 than recycling the old one, so that any erroneous accesses to the
456 old memory are reported. */
457 die_and_free_mem ( tid, mc, MC_MALLOC_REDZONE_SZB );
458
459 // Allocate a new chunk.
460 mc = create_MC_Chunk( ec, a_new, new_szB, MC_AllocMalloc );
461 }
462
463 p_new = (void*)a_new;
464
465 } else {
466 /* new size is bigger */
467 Addr a_new;
468 tl_assert(old_szB < new_szB);
469 /* Get new memory */
470 a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
471
472 if (a_new) {
473 UInt ecu;
474 ExeContext* ec;
475
476 ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
477 tl_assert(ec);
478 ecu = VG_(get_ECU_from_ExeContext)(ec);
479 tl_assert(VG_(is_plausible_ECU)(ecu));
480
481 /* First half kept and copied, second half new, red zones as normal */
482 MC_(make_mem_noaccess)( a_new-MC_MALLOC_REDZONE_SZB,
483 MC_MALLOC_REDZONE_SZB );
484 MC_(copy_address_range_state) ( (Addr)p_old, a_new, mc->szB );
485 MC_(make_mem_undefined_w_otag)( a_new+mc->szB, new_szB-mc->szB,
486 ecu | MC_OKIND_HEAP );
487 MC_(make_mem_noaccess) ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
488
489 /* Possibly fill new area with specified junk */
490 if (MC_(clo_malloc_fill) != -1) {
491 tl_assert(MC_(clo_malloc_fill) >= 0x00
492 && MC_(clo_malloc_fill) <= 0xFF);
493 VG_(memset)((void*)(a_new+old_szB), MC_(clo_malloc_fill),
494 new_szB-old_szB);
495 }
496
497 /* Copy from old to new */
498 VG_(memcpy)((void*)a_new, p_old, mc->szB);
499
500 /* Possibly fill freed area with specified junk. */
501 if (MC_(clo_free_fill) != -1) {
502 tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
503 VG_(memset)((void*)p_old, MC_(clo_free_fill), old_szB);
504 }
505
506 /* Free old memory */
507 /* Nb: we have to allocate a new MC_Chunk for the new memory rather
508 than recycling the old one, so that any erroneous accesses to the
509 old memory are reported. */
510 die_and_free_mem ( tid, mc, MC_MALLOC_REDZONE_SZB );
511
512 // Allocate a new chunk.
513 mc = create_MC_Chunk( ec, a_new, new_szB, MC_AllocMalloc );
514 }
515
516 p_new = (void*)a_new;
517 }
518
519 // Now insert the new mc (with a possibly new 'data' field) into
520 // malloc_list. If this realloc() did not increase the memory size, we
521 // will have removed and then re-added mc unnecessarily. But that's ok
522 // because shrinking a block with realloc() is (presumably) much rarer
523 // than growing it, and this way simplifies the growing case.
524 VG_(HT_add_node)( MC_(malloc_list), mc );
525
526 return p_new;
527 }
528
MC_(malloc_usable_size)529 SizeT MC_(malloc_usable_size) ( ThreadId tid, void* p )
530 {
531 MC_Chunk* mc = VG_(HT_lookup) ( MC_(malloc_list), (UWord)p );
532
533 // There may be slop, but pretend there isn't because only the asked-for
534 // area will be marked as addressable.
535 return ( mc ? mc->szB : 0 );
536 }
537
538 /* This handles the in place resize of a block, as performed by the
539 VALGRIND_RESIZEINPLACE_BLOCK client request. It is unrelated to,
540 and not used for, handling of the normal libc realloc()
541 function. */
MC_(handle_resizeInPlace)542 void MC_(handle_resizeInPlace)(ThreadId tid, Addr p,
543 SizeT oldSizeB, SizeT newSizeB, SizeT rzB)
544 {
545 MC_Chunk* mc = VG_(HT_lookup) ( MC_(malloc_list), (UWord)p );
546 if (!mc || mc->szB != oldSizeB || newSizeB == 0) {
547 /* Reject if: p is not found, or oldSizeB is wrong,
548 or new block would be empty. */
549 MC_(record_free_error) ( tid, p );
550 return;
551 }
552
553 if (oldSizeB == newSizeB)
554 return;
555
556 mc->szB = newSizeB;
557 if (newSizeB < oldSizeB) {
558 MC_(make_mem_noaccess)( p + newSizeB, oldSizeB - newSizeB + rzB );
559 } else {
560 ExeContext* ec = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
561 UInt ecu = VG_(get_ECU_from_ExeContext)(ec);
562 MC_(make_mem_undefined_w_otag)( p + oldSizeB, newSizeB - oldSizeB,
563 ecu | MC_OKIND_HEAP );
564 if (rzB > 0)
565 MC_(make_mem_noaccess)( p + newSizeB, rzB );
566 }
567 }
568
569
570 /*------------------------------------------------------------*/
571 /*--- Memory pool stuff. ---*/
572 /*------------------------------------------------------------*/
573
574 /* Set to 1 for intensive sanity checking. Is very expensive though
575 and should not be used in production scenarios. See #255966. */
576 #define MP_DETAILED_SANITY_CHECKS 0
577
578 static void check_mempool_sane(MC_Mempool* mp); /*forward*/
579
580
MC_(create_mempool)581 void MC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed)
582 {
583 MC_Mempool* mp;
584
585 if (VG_(clo_verbosity) > 2) {
586 VG_(message)(Vg_UserMsg, "create_mempool(0x%lx, %d, %d)\n",
587 pool, rzB, is_zeroed);
588 VG_(get_and_pp_StackTrace)
589 (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
590 }
591
592 mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
593 if (mp != NULL) {
594 VG_(tool_panic)("MC_(create_mempool): duplicate pool creation");
595 }
596
597 mp = VG_(malloc)("mc.cm.1", sizeof(MC_Mempool));
598 mp->pool = pool;
599 mp->rzB = rzB;
600 mp->is_zeroed = is_zeroed;
601 mp->chunks = VG_(HT_construct)( "MC_(create_mempool)" );
602 check_mempool_sane(mp);
603
604 /* Paranoia ... ensure this area is off-limits to the client, so
605 the mp->data field isn't visible to the leak checker. If memory
606 management is working correctly, anything pointer returned by
607 VG_(malloc) should be noaccess as far as the client is
608 concerned. */
609 if (!MC_(check_mem_is_noaccess)( (Addr)mp, sizeof(MC_Mempool), NULL )) {
610 VG_(tool_panic)("MC_(create_mempool): shadow area is accessible");
611 }
612
613 VG_(HT_add_node)( MC_(mempool_list), mp );
614 }
615
MC_(destroy_mempool)616 void MC_(destroy_mempool)(Addr pool)
617 {
618 MC_Chunk* mc;
619 MC_Mempool* mp;
620
621 if (VG_(clo_verbosity) > 2) {
622 VG_(message)(Vg_UserMsg, "destroy_mempool(0x%lx)\n", pool);
623 VG_(get_and_pp_StackTrace)
624 (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
625 }
626
627 mp = VG_(HT_remove) ( MC_(mempool_list), (UWord)pool );
628
629 if (mp == NULL) {
630 ThreadId tid = VG_(get_running_tid)();
631 MC_(record_illegal_mempool_error) ( tid, pool );
632 return;
633 }
634 check_mempool_sane(mp);
635
636 // Clean up the chunks, one by one
637 VG_(HT_ResetIter)(mp->chunks);
638 while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
639 /* Note: make redzones noaccess again -- just in case user made them
640 accessible with a client request... */
641 MC_(make_mem_noaccess)(mc->data-mp->rzB, mc->szB + 2*mp->rzB );
642 }
643 // Destroy the chunk table
644 VG_(HT_destruct)(mp->chunks);
645
646 VG_(free)(mp);
647 }
648
649 static Int
mp_compar(void * n1,void * n2)650 mp_compar(void* n1, void* n2)
651 {
652 MC_Chunk* mc1 = *(MC_Chunk**)n1;
653 MC_Chunk* mc2 = *(MC_Chunk**)n2;
654 if (mc1->data < mc2->data) return -1;
655 if (mc1->data > mc2->data) return 1;
656 return 0;
657 }
658
659 static void
check_mempool_sane(MC_Mempool * mp)660 check_mempool_sane(MC_Mempool* mp)
661 {
662 UInt n_chunks, i, bad = 0;
663 static UInt tick = 0;
664
665 MC_Chunk **chunks = (MC_Chunk**) VG_(HT_to_array)( mp->chunks, &n_chunks );
666 if (!chunks)
667 return;
668
669 if (VG_(clo_verbosity) > 1) {
670 if (tick++ >= 10000)
671 {
672 UInt total_pools = 0, total_chunks = 0;
673 MC_Mempool* mp2;
674
675 VG_(HT_ResetIter)(MC_(mempool_list));
676 while ( (mp2 = VG_(HT_Next)(MC_(mempool_list))) ) {
677 total_pools++;
678 VG_(HT_ResetIter)(mp2->chunks);
679 while (VG_(HT_Next)(mp2->chunks)) {
680 total_chunks++;
681 }
682 }
683
684 VG_(message)(Vg_UserMsg,
685 "Total mempools active: %d pools, %d chunks\n",
686 total_pools, total_chunks);
687 tick = 0;
688 }
689 }
690
691
692 VG_(ssort)((void*)chunks, n_chunks, sizeof(VgHashNode*), mp_compar);
693
694 /* Sanity check; assert that the blocks are now in order */
695 for (i = 0; i < n_chunks-1; i++) {
696 if (chunks[i]->data > chunks[i+1]->data) {
697 VG_(message)(Vg_UserMsg,
698 "Mempool chunk %d / %d is out of order "
699 "wrt. its successor\n",
700 i+1, n_chunks);
701 bad = 1;
702 }
703 }
704
705 /* Sanity check -- make sure they don't overlap */
706 for (i = 0; i < n_chunks-1; i++) {
707 if (chunks[i]->data + chunks[i]->szB > chunks[i+1]->data ) {
708 VG_(message)(Vg_UserMsg,
709 "Mempool chunk %d / %d overlaps with its successor\n",
710 i+1, n_chunks);
711 bad = 1;
712 }
713 }
714
715 if (bad) {
716 VG_(message)(Vg_UserMsg,
717 "Bad mempool (%d chunks), dumping chunks for inspection:\n",
718 n_chunks);
719 for (i = 0; i < n_chunks; ++i) {
720 VG_(message)(Vg_UserMsg,
721 "Mempool chunk %d / %d: %ld bytes "
722 "[%lx,%lx), allocated:\n",
723 i+1,
724 n_chunks,
725 chunks[i]->szB + 0UL,
726 chunks[i]->data,
727 chunks[i]->data + chunks[i]->szB);
728
729 VG_(pp_ExeContext)(chunks[i]->where);
730 }
731 }
732 VG_(free)(chunks);
733 }
734
MC_(mempool_alloc)735 void MC_(mempool_alloc)(ThreadId tid, Addr pool, Addr addr, SizeT szB)
736 {
737 MC_Mempool* mp;
738
739 if (VG_(clo_verbosity) > 2) {
740 VG_(message)(Vg_UserMsg, "mempool_alloc(0x%lx, 0x%lx, %ld)\n",
741 pool, addr, szB);
742 VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
743 }
744
745 mp = VG_(HT_lookup) ( MC_(mempool_list), (UWord)pool );
746 if (mp == NULL) {
747 MC_(record_illegal_mempool_error) ( tid, pool );
748 } else {
749 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
750 MC_(new_block)(tid, addr, szB, /*ignored*/0, mp->is_zeroed,
751 MC_AllocCustom, mp->chunks);
752 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
753 }
754 }
755
MC_(mempool_free)756 void MC_(mempool_free)(Addr pool, Addr addr)
757 {
758 MC_Mempool* mp;
759 MC_Chunk* mc;
760 ThreadId tid = VG_(get_running_tid)();
761
762 mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
763 if (mp == NULL) {
764 MC_(record_illegal_mempool_error)(tid, pool);
765 return;
766 }
767
768 if (VG_(clo_verbosity) > 2) {
769 VG_(message)(Vg_UserMsg, "mempool_free(0x%lx, 0x%lx)\n", pool, addr);
770 VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
771 }
772
773 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
774 mc = VG_(HT_remove)(mp->chunks, (UWord)addr);
775 if (mc == NULL) {
776 MC_(record_free_error)(tid, (Addr)addr);
777 return;
778 }
779
780 if (VG_(clo_verbosity) > 2) {
781 VG_(message)(Vg_UserMsg,
782 "mempool_free(0x%lx, 0x%lx) freed chunk of %ld bytes\n",
783 pool, addr, mc->szB + 0UL);
784 }
785
786 die_and_free_mem ( tid, mc, mp->rzB );
787 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
788 }
789
790
MC_(mempool_trim)791 void MC_(mempool_trim)(Addr pool, Addr addr, SizeT szB)
792 {
793 MC_Mempool* mp;
794 MC_Chunk* mc;
795 ThreadId tid = VG_(get_running_tid)();
796 UInt n_shadows, i;
797 VgHashNode** chunks;
798
799 if (VG_(clo_verbosity) > 2) {
800 VG_(message)(Vg_UserMsg, "mempool_trim(0x%lx, 0x%lx, %ld)\n",
801 pool, addr, szB);
802 VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
803 }
804
805 mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
806 if (mp == NULL) {
807 MC_(record_illegal_mempool_error)(tid, pool);
808 return;
809 }
810
811 check_mempool_sane(mp);
812 chunks = VG_(HT_to_array) ( mp->chunks, &n_shadows );
813 if (n_shadows == 0) {
814 tl_assert(chunks == NULL);
815 return;
816 }
817
818 tl_assert(chunks != NULL);
819 for (i = 0; i < n_shadows; ++i) {
820
821 Addr lo, hi, min, max;
822
823 mc = (MC_Chunk*) chunks[i];
824
825 lo = mc->data;
826 hi = mc->szB == 0 ? mc->data : mc->data + mc->szB - 1;
827
828 #define EXTENT_CONTAINS(x) ((addr <= (x)) && ((x) < addr + szB))
829
830 if (EXTENT_CONTAINS(lo) && EXTENT_CONTAINS(hi)) {
831
832 /* The current chunk is entirely within the trim extent: keep
833 it. */
834
835 continue;
836
837 } else if ( (! EXTENT_CONTAINS(lo)) &&
838 (! EXTENT_CONTAINS(hi)) ) {
839
840 /* The current chunk is entirely outside the trim extent:
841 delete it. */
842
843 if (VG_(HT_remove)(mp->chunks, (UWord)mc->data) == NULL) {
844 MC_(record_free_error)(tid, (Addr)mc->data);
845 VG_(free)(chunks);
846 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
847 return;
848 }
849 die_and_free_mem ( tid, mc, mp->rzB );
850
851 } else {
852
853 /* The current chunk intersects the trim extent: remove,
854 trim, and reinsert it. */
855
856 tl_assert(EXTENT_CONTAINS(lo) ||
857 EXTENT_CONTAINS(hi));
858 if (VG_(HT_remove)(mp->chunks, (UWord)mc->data) == NULL) {
859 MC_(record_free_error)(tid, (Addr)mc->data);
860 VG_(free)(chunks);
861 if (MP_DETAILED_SANITY_CHECKS) check_mempool_sane(mp);
862 return;
863 }
864
865 if (mc->data < addr) {
866 min = mc->data;
867 lo = addr;
868 } else {
869 min = addr;
870 lo = mc->data;
871 }
872
873 if (mc->data + szB > addr + szB) {
874 max = mc->data + szB;
875 hi = addr + szB;
876 } else {
877 max = addr + szB;
878 hi = mc->data + szB;
879 }
880
881 tl_assert(min <= lo);
882 tl_assert(lo < hi);
883 tl_assert(hi <= max);
884
885 if (min < lo && !EXTENT_CONTAINS(min)) {
886 MC_(make_mem_noaccess)( min, lo - min);
887 }
888
889 if (hi < max && !EXTENT_CONTAINS(max)) {
890 MC_(make_mem_noaccess)( hi, max - hi );
891 }
892
893 mc->data = lo;
894 mc->szB = (UInt) (hi - lo);
895 VG_(HT_add_node)( mp->chunks, mc );
896 }
897
898 #undef EXTENT_CONTAINS
899
900 }
901 check_mempool_sane(mp);
902 VG_(free)(chunks);
903 }
904
MC_(move_mempool)905 void MC_(move_mempool)(Addr poolA, Addr poolB)
906 {
907 MC_Mempool* mp;
908
909 if (VG_(clo_verbosity) > 2) {
910 VG_(message)(Vg_UserMsg, "move_mempool(0x%lx, 0x%lx)\n", poolA, poolB);
911 VG_(get_and_pp_StackTrace)
912 (VG_(get_running_tid)(), MEMPOOL_DEBUG_STACKTRACE_DEPTH);
913 }
914
915 mp = VG_(HT_remove) ( MC_(mempool_list), (UWord)poolA );
916
917 if (mp == NULL) {
918 ThreadId tid = VG_(get_running_tid)();
919 MC_(record_illegal_mempool_error) ( tid, poolA );
920 return;
921 }
922
923 mp->pool = poolB;
924 VG_(HT_add_node)( MC_(mempool_list), mp );
925 }
926
MC_(mempool_change)927 void MC_(mempool_change)(Addr pool, Addr addrA, Addr addrB, SizeT szB)
928 {
929 MC_Mempool* mp;
930 MC_Chunk* mc;
931 ThreadId tid = VG_(get_running_tid)();
932
933 if (VG_(clo_verbosity) > 2) {
934 VG_(message)(Vg_UserMsg, "mempool_change(0x%lx, 0x%lx, 0x%lx, %ld)\n",
935 pool, addrA, addrB, szB);
936 VG_(get_and_pp_StackTrace) (tid, MEMPOOL_DEBUG_STACKTRACE_DEPTH);
937 }
938
939 mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
940 if (mp == NULL) {
941 MC_(record_illegal_mempool_error)(tid, pool);
942 return;
943 }
944
945 check_mempool_sane(mp);
946
947 mc = VG_(HT_remove)(mp->chunks, (UWord)addrA);
948 if (mc == NULL) {
949 MC_(record_free_error)(tid, (Addr)addrA);
950 return;
951 }
952
953 mc->data = addrB;
954 mc->szB = szB;
955 VG_(HT_add_node)( mp->chunks, mc );
956
957 check_mempool_sane(mp);
958 }
959
MC_(mempool_exists)960 Bool MC_(mempool_exists)(Addr pool)
961 {
962 MC_Mempool* mp;
963
964 mp = VG_(HT_lookup)(MC_(mempool_list), (UWord)pool);
965 if (mp == NULL) {
966 return False;
967 }
968 return True;
969 }
970
971
972 /*------------------------------------------------------------*/
973 /*--- Statistics printing ---*/
974 /*------------------------------------------------------------*/
975
MC_(print_malloc_stats)976 void MC_(print_malloc_stats) ( void )
977 {
978 MC_Chunk* mc;
979 SizeT nblocks = 0;
980 ULong nbytes = 0;
981
982 if (VG_(clo_verbosity) == 0)
983 return;
984 if (VG_(clo_xml))
985 return;
986
987 /* Count memory still in use. */
988 VG_(HT_ResetIter)(MC_(malloc_list));
989 while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) {
990 nblocks++;
991 nbytes += (ULong)mc->szB;
992 }
993
994 VG_(umsg)(
995 "HEAP SUMMARY:\n"
996 " in use at exit: %'llu bytes in %'lu blocks\n"
997 " total heap usage: %'lu allocs, %'lu frees, %'llu bytes allocated\n"
998 "\n",
999 nbytes, nblocks,
1000 cmalloc_n_mallocs,
1001 cmalloc_n_frees, cmalloc_bs_mallocd
1002 );
1003 }
1004
1005 /*--------------------------------------------------------------------*/
1006 /*--- end ---*/
1007 /*--------------------------------------------------------------------*/
1008