1 /*
2 * Memory chunk low-level allocation routines
3 *
4 * Copyright 2020 by Gray Watson
5 *
6 * This file is part of the dmalloc package.
7 *
8 * Permission to use, copy, modify, and distribute this software for
9 * any purpose and without fee is hereby granted, provided that the
10 * above copyright notice and this permission notice appear in all
11 * copies, and that the name of Gray Watson not be used in advertising
12 * or publicity pertaining to distribution of the document or software
13 * without specific, written prior permission.
14 *
15 * Gray Watson makes no representations about the suitability of the
16 * software described herein for any purpose. It is provided "as is"
17 * without express or implied warranty.
18 *
19 * The author may be contacted via http://dmalloc.com/
20 */
21
22 /*
23 * This file contains algorithm level routine for the heap. They handle the
24 * manipulation and administration of chunks of memory.
25 */
26
27 #include <ctype.h>
28
29 #if HAVE_STRING_H
30 # include <string.h>
31 #endif
32 #if HAVE_STDLIB_H
33 # include <stdlib.h>
34 #endif
35
36 #define DMALLOC_DISABLE
37
38 #include "conf.h"
39
40 #if LOG_PNT_TIMEVAL
41 #ifdef TIMEVAL_INCLUDE
42 # include TIMEVAL_INCLUDE
43 #endif
44 #else
45 # if LOG_PNT_TIME
46 # ifdef TIME_INCLUDE
47 # include TIME_INCLUDE
48 # endif
49 # endif
50 #endif
51
52 #include "dmalloc.h"
53
54 #include "append.h"
55 #include "chunk.h"
56 #include "chunk_loc.h"
57 #include "compat.h"
58 #include "debug_tok.h"
59 #include "dmalloc_loc.h"
60 #include "dmalloc_rand.h"
61 #include "dmalloc_tab.h"
62 #include "error.h"
63 #include "error_val.h"
64 #include "heap.h"
65
66 /*
67 * Library Copyright and URL information for ident and what programs
68 */
69 #if IDENT_WORKS
70 #ident "@(#) $Copyright: Dmalloc package Copyright 2020 by Gray Watson $"
71 #ident "@(#) $URL: Source for dmalloc available from http://dmalloc.com/ $"
72 #else
73 static char *copyright =
74 "@(#) $Copyright: Dmalloc package Copyright 2020 by Gray Watson $";
75 static char *source_url =
76 "@(#) $URL: Source for dmalloc available from http://dmalloc.com/ $";
77 #endif
78
79 #if LOCK_THREADS
80 #if IDENT_WORKS
81 #ident "@(#) $Information: lock-threads is enabled $"
82 #else
83 static char *information = "@(#) $Information: lock-threads is enabled $";
84 #endif
85 #endif
86
87 /*
88 * exported variables
89 */
90 /* limit in how much memory we are allowed to allocate */
91 unsigned long _dmalloc_memory_limit = 0;
92
93 /* total number of bytes that the heap has allocated */
94 unsigned long _dmalloc_alloc_total = 0;
95
96 /*
97 * local variables
98 */
99
100 /*
101 * Skip list of our free list sorted by size in bytes. Bit of a hack
102 * here. Basically we cannot do a alloc for the structure and we'd
103 * like it to be static storage so we allocate an array of them to
104 * make sure we have enough forward pointers, when all we need is
105 * SKIP_SLOT_SIZE(MAX_SKIP_LEVEL + 1) bytes.
106 */
107 static skip_alloc_t skip_free_alloc[MAX_SKIP_LEVEL /* read note ^^ */];
108 static skip_alloc_t *skip_free_list = skip_free_alloc;
109
110 /* skip list of all of our allocated blocks sorted by address */
111 static skip_alloc_t skip_address_alloc[MAX_SKIP_LEVEL /* read note ^^ */];
112 static skip_alloc_t *skip_address_list = skip_address_alloc;
113
114 /* update slots which we use to update the skip lists */
115 static skip_alloc_t skip_update[MAX_SKIP_LEVEL /* read note ^^ */];
116
117 /* linked list of slots of various sizes */
118 static skip_alloc_t *entry_free_list[MAX_SKIP_LEVEL];
119 /* linked list of blocks of the sizes */
120 static entry_block_t *entry_blocks[MAX_SKIP_LEVEL];
121 /* linked list of freed blocks on hold waiting for the FREED_POINTER_DELAY */
122 static skip_alloc_t *free_wait_list_head = NULL;
123 static skip_alloc_t *free_wait_list_tail = NULL;
124
125 /* administrative structures */
126 static char fence_bottom[FENCE_BOTTOM_SIZE];
127 static char fence_top[FENCE_TOP_SIZE];
128 static int bit_sizes[BASIC_BLOCK]; /* number bits for div-blocks*/
129
130 /* memory tables */
131 static mem_table_t mem_table_alloc;
132 static mem_entry_t mem_table_alloc_entries[MEM_ALLOC_ENTRIES];
133 static mem_table_t mem_table_changed;
134 static mem_entry_t mem_table_changed_entries[MEM_ALLOC_ENTRIES];
135
136 /* memory stats */
137 static unsigned long alloc_current = 0; /* current memory usage */
138 static unsigned long alloc_maximum = 0; /* maximum memory usage */
139 static unsigned long alloc_cur_given = 0; /* current mem given */
140 static unsigned long alloc_max_given = 0; /* maximum mem given */
141 static unsigned long alloc_one_max = 0; /* maximum at once */
142 static unsigned long free_space_bytes = 0; /* count the free bytes */
143
144 /* pointer stats */
145 static unsigned long alloc_cur_pnts = 0; /* current pointers */
146 static unsigned long alloc_max_pnts = 0; /* maximum pointers */
147 static unsigned long alloc_tot_pnts = 0; /* total pointers */
148
149 /* admin counts */
150 static unsigned long heap_check_c = 0; /* count of heap-checks */
151 static unsigned long user_block_c = 0; /* count of blocks */
152 static unsigned long admin_block_c = 0; /* count of admin blocks */
153
154 /* alloc counts */
155 static unsigned long func_malloc_c = 0; /* count the mallocs */
156 static unsigned long func_calloc_c = 0; /* # callocs, done in alloc */
157 static unsigned long func_realloc_c = 0; /* count the reallocs */
158 static unsigned long func_recalloc_c = 0; /* count the reallocs */
159 static unsigned long func_memalign_c = 0; /* count the memaligns */
160 static unsigned long func_valloc_c = 0; /* count the veallocs */
161 static unsigned long func_new_c = 0; /* count the news */
162 static unsigned long func_free_c = 0; /* count the frees */
163 static unsigned long func_delete_c = 0; /* count the deletes */
164
165 /**************************** skip list routines *****************************/
166
167 /*
168 * static int random_level
169 *
170 * Return a random level to be associated with a new free-list entry.
171 *
172 * Returns random level from 0 to max_level - 1.
173 *
174 * ARGUMENTS:
175 *
176 * max_level -> Maximum level of the free-list.
177 */
random_level(const int max_level)178 static int random_level(const int max_level)
179 {
180 int level_c;
181
182 for (level_c = 0; level_c < max_level; level_c++) {
183 /*
184 * Basically we count the number of times that the random number
185 * generator returns an odd number in a row. On average this
186 * should return 0 1/2 the time, 1 1/4 of the time, 2 1/8 of a
187 * time, and N 1/(2^(N - 1)) of the time. This is what we want.
188 * We could test for this in the configure scripts.
189 *
190 * Since many machines return random numbers which aren't that
191 * random, there may be better ways of doing this. In the past I
192 * had (_dmalloc_rand() % 10000 >= 5000) or something but I'd
193 * rather not have the % overhead here.
194 */
195 if (_dmalloc_rand() & 1) {
196 break;
197 }
198 }
199
200 return level_c;
201 }
202
203 /*
204 * static skip_alloc_t *find_address
205 *
206 * Look for a specific address in the skip list. If it exist then a
207 * pointer to the matching slot is returned otherwise NULL. Either
208 * way, the links that were traversed to get there are set in the
209 * update slot which has the maximum number of levels.
210 *
211 * RETURNS:
212 *
213 * Success - Pointer to the slot which matches the block-num and size
214 * pair.
215 *
216 * Failure - NULL and this will not set dmalloc_errno
217 *
218 * ARGUMENTS:
219 *
220 * address -> Address we are looking for.
221 *
222 * free_b -> Look on the free list otherwise look on the used list.
223 *
224 * exact_b -> Set to 1 to find the exact pointer. If 0 then the
225 * address could be inside a block.
226 *
227 * update_p -> Pointer to the skip_alloc entry we are using to hold
228 * the update pointers.
229 */
find_address(const void * address,const int free_b,const int exact_b,skip_alloc_t * update_p)230 static skip_alloc_t *find_address(const void *address, const int free_b,
231 const int exact_b,
232 skip_alloc_t *update_p)
233 {
234 int level_c;
235 skip_alloc_t *slot_p, *found_p = NULL, *next_p;
236
237 /* skip_address_max_level */
238 level_c = MAX_SKIP_LEVEL - 1;
239 if (free_b) {
240 slot_p = skip_free_list;
241 }
242 else {
243 slot_p = skip_address_list;
244 }
245
246 /* traverse list to smallest entry */
247 while (1) {
248
249 /* next on we are looking for */
250 next_p = slot_p->sa_next_p[level_c];
251
252 /*
253 * sort by address
254 */
255
256 /* are we are at the end of a row? */
257 if (next_p == NULL) {
258 /* just go down a level */
259 }
260 else if (next_p == found_p
261 || (char *)next_p->sa_mem > (char *)address) {
262 /* just go down a level */
263 }
264 else if ((char *)next_p->sa_mem == (char *)address) {
265 /* found it and go down a level */
266 found_p = next_p;
267 }
268 /*
269 * (char *)next_p->sa_mem < (char *)address
270 */
271 else if ((! exact_b)
272 && ((char *)next_p->sa_mem + next_p->sa_total_size >
273 (char *)address)) {
274 /*
275 * if we are doing loose searches and this block contains this
276 * pointer then we have a match
277 */
278 found_p = next_p;
279 }
280 else {
281 /* next slot is less, go right */
282 slot_p = next_p;
283 continue;
284 }
285
286 /* we are lowering the level */
287
288 update_p->sa_next_p[level_c] = slot_p;
289 if (level_c == 0) {
290 break;
291 }
292 level_c--;
293 }
294
295 return found_p;
296 }
297
298 /*
299 * static skip_alloc_t *find_free_size
300 *
301 * Look for a specific size in the free skip list. If it exist then a
302 * pointer to the matching slot is returned otherwise NULL. Either
303 * way, the links that were traversed to get there are set in the
304 * update slot which has the maximum number of levels.
305 *
306 * Returns a pointer to the slot which matches the size pair on
307 * success or NULL on failure.
308 *
309 * ARGUMENTS:
310 *
311 * address -> Address we are looking for.
312 *
313 * update_p -> Pointer to the skip_alloc entry we are using to hold
314 * the update pointers.
315 */
find_free_size(const unsigned int size,skip_alloc_t * update_p)316 static skip_alloc_t *find_free_size(const unsigned int size,
317 skip_alloc_t *update_p)
318 {
319 int level_c, cmp;
320 skip_alloc_t *slot_p, *found_p = NULL, *next_p;
321
322 /* skip_free_max_level */
323 level_c = MAX_SKIP_LEVEL - 1;
324 slot_p = skip_free_list;
325
326 /* traverse list to smallest entry */
327 while (1) {
328
329 /* next on we are looking for */
330 next_p = slot_p->sa_next_p[level_c];
331
332 /* are we are at the end of a row? */
333 if (next_p == NULL
334 || next_p == found_p) {
335 /* just go down a level */
336 }
337 else {
338 cmp = next_p->sa_total_size - size;
339 if (cmp < 0) {
340 /* next slot is less, go right */
341 slot_p = next_p;
342 continue;
343 }
344 else if (cmp == 0) {
345 /*
346 * we found a match but it may not be the first slot with this
347 * size and we want the first match
348 */
349 found_p = next_p;
350 }
351 }
352
353 /* we are lowering the level */
354
355 update_p->sa_next_p[level_c] = slot_p;
356 if (level_c == 0) {
357 break;
358 }
359 level_c--;
360 }
361
362 /* space should be free */
363 if (found_p != NULL && (! BIT_IS_SET(found_p->sa_flags, ALLOC_FLAG_FREE))) {
364 /* sanity check */
365 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
366 dmalloc_error("find_free_size");
367 return NULL;
368 }
369
370 return found_p;
371 }
372
373 /*
374 * static int insert_slot
375 *
376 * Insert an address entry into a skip list.
377 *
378 * Returns 1 on success or 0 on failure.
379 *
380 * ARGUMENTS:
381 *
382 * slot_p <-> Slot that we are inserting into the skip list.
383 *
384 * free_b -> Insert a free address in the free-size list otherwise it
385 * will go into the used address list.
386 */
insert_slot(skip_alloc_t * slot_p,const int free_b)387 static int insert_slot(skip_alloc_t *slot_p, const int free_b)
388 {
389 skip_alloc_t *adjust_p, *update_p;
390 int level_c;
391
392 update_p = skip_update;
393
394 if (free_b) {
395 (void)find_free_size(slot_p->sa_total_size, update_p);
396 /*
397 * NOTE: we can get a new_p because there might be other blocks of
398 * the same size which we will be inserting before.
399 */
400 }
401 else if (find_address(slot_p->sa_mem, 0 /* used list */, 1 /* exact */,
402 update_p) != NULL) {
403 /*
404 * Sanity check. We should not have found it since that means
405 * that someone has the same size and block-num.
406 */
407 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
408 dmalloc_error("insert_slot");
409 return 0;
410 }
411
412 /* update the block skip list */
413 for (level_c = 0; level_c <= slot_p->sa_level_n; level_c++) {
414 /*
415 * We are inserting our new slot after each of the slots in the
416 * update array. So for each level, we get the slot we are
417 * adjusting, we take it's next pointers and set them in the new
418 * slot, and we point its next pointers to the new slot.
419 */
420 adjust_p = update_p->sa_next_p[level_c];
421 slot_p->sa_next_p[level_c] = adjust_p->sa_next_p[level_c];
422 adjust_p->sa_next_p[level_c] = slot_p;
423 }
424
425 return 1;
426 }
427
428 /*
429 * static int alloc_slots
430 *
431 * Allocate a block of new slots of a certain size and add them to the
432 * free list. If there are none in the linked list then we will
433 * allocate a block of the size.
434 *
435 * Returns a valid pointer to a single block that was allocated for
436 * the slots on success or NULL on failure.
437 *
438 * ARGUMENTS:
439 *
440 * level_n -> Number of the level we are looking for. Set to 0 to
441 * have it be chosen at random.
442 */
alloc_slots(const int level_n)443 static void *alloc_slots(const int level_n)
444 {
445 skip_alloc_t *new_p;
446 entry_block_t *block_p;
447 unsigned int *magic3_p, magic3;
448 int size, new_c;
449
450 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_ADMIN)) {
451 dmalloc_message("need a block of slots for level %d", level_n);
452 }
453
454 /* we need to allocate a new block of the slots of this level */
455 block_p = _dmalloc_heap_alloc(BLOCK_SIZE);
456 if (block_p == NULL) {
457 /*
458 * Sanity check. Out of heap memory. Error code set in
459 * _dmalloc_heap_alloc().
460 */
461 return NULL;
462 }
463 memset(block_p, 0, BLOCK_SIZE);
464 admin_block_c++;
465
466 /* intialize the block structure */
467 block_p->eb_magic1 = ENTRY_BLOCK_MAGIC1;
468 block_p->eb_level_n = level_n;
469 block_p->eb_magic2 = ENTRY_BLOCK_MAGIC2;
470
471 /* add the block on the entry block linked list */
472 block_p->eb_next_p = entry_blocks[level_n];
473 entry_blocks[level_n] = block_p;
474
475 /* put the magic3 at the end of the block */
476 magic3_p = (unsigned int *)((char *)block_p + BLOCK_SIZE -
477 sizeof(*magic3_p));
478 magic3 = ENTRY_BLOCK_MAGIC3;
479 memcpy(magic3_p, &magic3, sizeof(*magic3_p));
480
481 /* get the size of the slot */
482 size = SKIP_SLOT_SIZE(level_n);
483
484 /* add in all of the unused slots to the linked list */
485 new_c = 1;
486 for (new_p = &block_p->eb_first_slot;
487 (char *)new_p + size < (char *)magic3_p;
488 new_p = (skip_alloc_t *)((char *)new_p + size)) {
489 new_p->sa_level_n = level_n;
490 new_p->sa_next_p[0] = entry_free_list[level_n];
491 entry_free_list[level_n] = new_p;
492 new_c++;
493 }
494
495 /* extern pointer information set in _dmalloc_heap_alloc */
496 return block_p;
497 }
498
499 /*
500 * static int remove_slot
501 *
502 * Remove a slot from the skip list.
503 *
504 * Returns 1 on success or 0 on failure.
505 *
506 * ARGUMENTS:
507 *
508 * delete_p -> Pointer to the block we are deleting from the list.
509 *
510 * update_p -> Pointer to the skip_alloc entry we are using to hold
511 * the update pointers.
512 */
remove_slot(skip_alloc_t * delete_p,skip_alloc_t * update_p)513 static int remove_slot(skip_alloc_t *delete_p, skip_alloc_t *update_p)
514 {
515 skip_alloc_t *adjust_p;
516 int level_c;
517
518 /* update the block skip list */
519 for (level_c = 0; level_c <= MAX_SKIP_LEVEL; level_c++) {
520
521 /*
522 * The update node holds pointers to the slots which are pointing
523 * to the one we want since we need to update those pointers
524 * ahead.
525 */
526 adjust_p = update_p->sa_next_p[level_c];
527
528 /*
529 * If the pointer in question is not pointing to the deleted slot
530 * then the deleted slot is shorter than this level and we are
531 * done. This is guaranteed if we have a proper skip list.
532 */
533 if (adjust_p->sa_next_p[level_c] != delete_p) {
534 break;
535 }
536
537 /*
538 * We are deleting a slot after each of the slots in the update
539 * array. So for each level, we get the slot we are adjusting, we
540 * set it's next pointers to the next pointers at the same level
541 * from the deleted slot.
542 */
543 adjust_p->sa_next_p[level_c] = delete_p->sa_next_p[level_c];
544 }
545
546 /*
547 * Sanity check here, we should always have at least 1 pointer to
548 * the found node that we are deleting.
549 */
550 if (level_c == 0) {
551 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
552 dmalloc_error("remove_slot");
553 return 0;
554 }
555
556 return 1;
557 }
558
559 /*
560 * static skip_alloc_t *get_slot
561 *
562 * Get a new slot of a certain size. This calls alloc_slot and then
563 * does a whole bunch of things if alloc_slots generates the need for
564 * two new slots. Jumping through hoops to get this right.
565 *
566 * Returns a valid skip-alloc pointer on success or NULL on failure.
567 */
get_slot(void)568 static skip_alloc_t *get_slot(void)
569 {
570 skip_alloc_t *new_p;
571 int level_n, slot_size;
572 void *admin_mem;
573
574 /* generate the level for our new slot */
575 level_n = random_level(MAX_SKIP_LEVEL);
576 slot_size = SKIP_SLOT_SIZE(level_n);
577
578 /* get an extry from the free list */
579 new_p = entry_free_list[level_n];
580 if (new_p != NULL) {
581 /* shift the linked list over */
582 entry_free_list[level_n] = new_p->sa_next_p[0];
583 /* zero our slot entry */
584 memset(new_p, 0, slot_size);
585 new_p->sa_level_n = level_n;
586 return new_p;
587 }
588
589 /*
590 * Okay, this is a little wild. Holding on?
591 *
592 * So we are trying to get a slot of a certain size to store
593 * something in a skip list. We didn't have any on the free-list so
594 * now we will allocate a block. We allocate a block of memory to
595 * hold the slots meaning that we may need 1 new slot to account for
596 * the admin and external memory in addition to the 1 requested.
597 *
598 * To do it right, would take a recursive call to get_slot which I
599 * am not willing to do so we will have 2 blocks in a row which have
600 * the same height. This is less than efficient but oh well.
601 */
602
603 /* add in all of the unused slots to the linked list */
604 admin_mem = alloc_slots(level_n);
605 if (admin_mem == NULL) {
606 /* Sanity check. Error code set in alloc_slots(). */
607 return NULL;
608 }
609
610 /* get one for the admin memory */
611 new_p = entry_free_list[level_n];
612 if (new_p == NULL) {
613 /*
614 * Sanity check. We should have created a whole bunch of
615 * addresses.
616 */
617 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
618 dmalloc_error("get_slot");
619 return NULL;
620 }
621 entry_free_list[level_n] = new_p->sa_next_p[0];
622 memset(new_p, 0, slot_size);
623 new_p->sa_flags = ALLOC_FLAG_ADMIN;
624 new_p->sa_mem = admin_mem;
625 new_p->sa_total_size = BLOCK_SIZE;
626 new_p->sa_level_n = level_n;
627
628 /* now put it in the used list */
629 if (! insert_slot(new_p, 0 /* used list */)) {
630 /* Sanity check. error code set in insert_slot(). */
631 return NULL;
632 }
633
634 /* now get one for the user */
635 new_p = entry_free_list[level_n];
636 if (new_p == NULL) {
637 /*
638 * Sanity check. We should have created a whole bunch of
639 * addresses.
640 */
641 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
642 dmalloc_error("get_slot");
643 return NULL;
644 }
645 entry_free_list[level_n] = new_p->sa_next_p[0];
646 memset(new_p, 0, slot_size);
647 new_p->sa_level_n = level_n;
648
649 /* level_np set up top */
650 return new_p;
651 }
652
653 /*
654 * static skip_alloc_t *insert_address
655 *
656 * Insert an address entry into a skip list.
657 *
658 * Returns a valid slot pointer on sucess or NULL on failure.
659 *
660 * ARGUMENTS:
661 *
662 * address -> Address we are inserting into the address list.
663 *
664 * free_b -> Insert a free address in the free-size list otherwise it
665 * will go into the used address list.
666 *
667 * tot_size -> Total size of the chunk that we are inserting into the
668 * list.
669 */
insert_address(void * address,const int free_b,const unsigned int tot_size)670 static skip_alloc_t *insert_address(void *address, const int free_b,
671 const unsigned int tot_size)
672 {
673 skip_alloc_t *new_p;
674
675 /* get a new entry */
676 new_p = get_slot();
677 if (new_p == NULL) {
678 /* error code set in get_slot */
679 return NULL;
680 }
681 if (free_b) {
682 new_p->sa_flags = ALLOC_FLAG_FREE;
683 }
684 else {
685 new_p->sa_flags = ALLOC_FLAG_USER;
686 }
687 new_p->sa_mem = address;
688 new_p->sa_total_size = tot_size;
689
690 /* now try and insert the slot into the skip-list */
691 if (! insert_slot(new_p, free_b)) {
692 /* Sanity check. error code set in insert_slot(). */
693 return NULL;
694 }
695
696 return new_p;
697 }
698
699 /******************************* misc routines *******************************/
700
701 /*
702 * static int expand_chars
703 *
704 * Copies a buffer into a output buffer while translates
705 * non-printables into %03o octal values. If it can, it will also
706 * translate certain \ characters (\r, \n, etc.) into \\%c. The
707 * routine is useful for printing out binary values.
708 *
709 * Note: It does _not_ add a \0 at the end of the output buffer.
710 *
711 * Returns the number of characters added to the output buffer.
712 *
713 * ARGUMENTS:
714 *
715 * buf - the buffer to convert.
716 *
717 * buf_size - size of the buffer. If < 0 then it will expand till it
718 * sees a \0 character.
719 *
720 * out - destination buffer for the convertion.
721 *
722 * out_size - size of the output buffer.
723 */
expand_chars(const void * buf,const int buf_size,char * out,const int out_size)724 static int expand_chars(const void *buf, const int buf_size,
725 char *out, const int out_size)
726 {
727 const unsigned char *buf_p, *spec_p;
728 char *out_p = out, *bounds_p;
729
730 /* setup our max pointer */
731 bounds_p = out + out_size;
732
733 /* run through the input buffer, counting the characters as we go */
734 for (buf_p = (const unsigned char *)buf;
735 buf_p < (const unsigned char *)buf + buf_size;
736 buf_p++) {
737
738 /* search for special characters */
739 for (spec_p = (unsigned char *)SPECIAL_CHARS + 1;
740 *(spec_p - 1) != '\0';
741 spec_p += 2) {
742 if (*spec_p == *buf_p) {
743 break;
744 }
745 }
746
747 /* did we find one? */
748 if (*(spec_p - 1) != '\0') {
749 if (out_p + 2 >= bounds_p) {
750 break;
751 }
752 out_p = append_format(out_p, bounds_p, "\\%c", *(spec_p - 1));
753 continue;
754 }
755
756 /* print out any 7-bit printable characters */
757 if (*buf_p < 128 && isprint(*buf_p)) {
758 if (out_p + 1 >= bounds_p) {
759 break;
760 }
761 *out_p = *(char *)buf_p;
762 out_p += 1;
763 }
764 else {
765 if (out_p + 4 >= bounds_p) {
766 break;
767 }
768 out_p = append_format(out_p, bounds_p, "\\%03o", *buf_p);
769 }
770 }
771 /* try to punch the null if we have space in case the %.*s doesn't work */
772 if (out_p < bounds_p) {
773 *out_p = '\0';
774 }
775
776 return out_p - out;
777 }
778
779 /*
780 * static void get_pnt_info
781 *
782 * With a slot, set a number of pointers to places within the block.
783 *
784 * ARGUMENTS:
785 *
786 * slot_p -> Pointer to a slot structure that we are getting info on.
787 *
788 * info_p <-> Pointer to an info structure that we are filling with
789 * information.
790 */
get_pnt_info(const skip_alloc_t * slot_p,pnt_info_t * info_p)791 static void get_pnt_info(const skip_alloc_t *slot_p, pnt_info_t *info_p)
792 {
793 info_p->pi_fence_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FENCE);
794 info_p->pi_valloc_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_VALLOC);
795 info_p->pi_blanked_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK);
796
797 info_p->pi_alloc_start = slot_p->sa_mem;
798
799 if (info_p->pi_fence_b) {
800 if (info_p->pi_valloc_b) {
801 info_p->pi_user_start = (char *)info_p->pi_alloc_start + BLOCK_SIZE;
802 info_p->pi_fence_bottom = (char *)info_p->pi_user_start -
803 FENCE_BOTTOM_SIZE;
804 }
805 else {
806 info_p->pi_fence_bottom = info_p->pi_alloc_start;
807 info_p->pi_user_start = (char *)info_p->pi_alloc_start +
808 FENCE_BOTTOM_SIZE;
809 }
810 }
811 else {
812 info_p->pi_fence_bottom = NULL;
813 info_p->pi_user_start = info_p->pi_alloc_start;
814 }
815
816 info_p->pi_user_bounds = (char *)info_p->pi_user_start +
817 slot_p->sa_user_size;
818
819 info_p->pi_alloc_bounds = (char *)slot_p->sa_mem + slot_p->sa_total_size;
820
821 if (info_p->pi_fence_b) {
822 info_p->pi_fence_top = info_p->pi_user_bounds;
823 info_p->pi_upper_bounds = (char *)info_p->pi_alloc_bounds - FENCE_TOP_SIZE;
824 }
825 else {
826 info_p->pi_fence_top = NULL;
827 info_p->pi_upper_bounds = info_p->pi_alloc_bounds;
828 }
829 }
830
831 /*
832 * static char *display_pnt
833 *
834 * Write into a buffer a discription of a pointer.
835 *
836 * Returns a pointer to buffer 1st argument.
837 *
838 * ARGUMENTS:
839 *
840 * user_pnt -> Pointer that we are displaying.
841 *
842 * alloc_p -> Pointer to the skip slot which we are displaying.
843 *
844 * buf <-> Passed in buffer which will be filled with a description of
845 * the pointer.
846 *
847 * buf_size -> Size of the buffer in bytes.
848 */
display_pnt(const void * user_pnt,const skip_alloc_t * alloc_p,char * buf,const int buf_size)849 static char *display_pnt(const void *user_pnt, const skip_alloc_t *alloc_p,
850 char *buf, const int buf_size)
851 {
852 char *buf_p, *bounds_p;
853 int elapsed_b;
854
855 buf_p = buf;
856 bounds_p = buf_p + buf_size;
857
858 buf_p = append_format(buf_p, bounds_p, "%p", user_pnt);
859
860 #if LOG_PNT_SEEN_COUNT
861 buf_p = append_format(buf_p, bounds_p, "|s%lu", alloc_p->sa_seen_c);
862 #endif
863
864 #if LOG_PNT_ITERATION
865 buf_p = append_format(buf_p, bounds_p, "|i%lu", alloc_p->sa_iteration);
866 #endif
867
868 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_ELAPSED_TIME)) {
869 elapsed_b = 1;
870 }
871 else {
872 elapsed_b = 0;
873 }
874 if (elapsed_b || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_CURRENT_TIME)) {
875 #if LOG_PNT_TIMEVAL
876 {
877 char time_buf[64];
878 buf_p = append_format(buf_p, bounds_p, "|w%s",
879 _dmalloc_ptimeval(&alloc_p->sa_timeval, time_buf,
880 sizeof(time_buf), elapsed_b));
881 }
882 #else
883 #if LOG_PNT_TIME
884 {
885 char time_buf[64];
886 buf_p = append_format(buf_p, bounds_p, "|w%s",
887 _dmalloc_ptime(&alloc_p->sa_time, time_buf,
888 sizeof(time_buf), elapsed_b));
889 }
890 #endif
891 #endif
892 }
893
894 #if LOG_PNT_THREAD_ID
895 {
896 char thread_id[256];
897
898 buf_p = append_string(buf_p, bounds_p, "|t");
899 THREAD_ID_TO_STRING(thread_id, sizeof(thread_id), alloc_p->sa_thread_id);
900 buf_p = append_string(buf_p, bounds_p, thread_id);
901 }
902 #endif
903 append_null(buf_p, bounds_p);
904
905 return buf;
906 }
907
908 /*
909 * static void log_error_info
910 *
911 * Logging information about a pointer, usually during an error condition.
912 *
913 * ARGUMENTS:
914 *
915 * now_file -> File from where we generated the error.
916 *
917 * now_line -> Line number from where we generated the error.
918 *
919 * user_pnt -> Pointer in question. This can be 0L then it will use
920 * the slot_p memory pointer.
921 *
922 * slot_p -> Pointer to the slot associated with the user_pnt or 0L.
923 *
924 * reason -> Reason string why something happened.
925 *
926 * where -> What routine is calling log_error_info. For instance
927 * malloc or chunk_check.
928 */
log_error_info(const char * now_file,const unsigned int now_line,const void * user_pnt,const skip_alloc_t * slot_p,const char * reason,const char * where)929 static void log_error_info(const char *now_file,
930 const unsigned int now_line,
931 const void *user_pnt,
932 const skip_alloc_t *slot_p,
933 const char *reason, const char *where)
934 {
935 static int dump_bottom_b = 0, dump_top_b = 0;
936 char out[(DUMP_SPACE + FENCE_BOTTOM_SIZE + FENCE_TOP_SIZE) * 4];
937 char where_buf[MAX_FILE_LENGTH + 64];
938 char where_buf2[MAX_FILE_LENGTH + 64];
939 const char *prev_file;
940 const void *dump_pnt = user_pnt;
941 const void *start_user;
942 unsigned int prev_line, user_size;
943 skip_alloc_t *other_p;
944 pnt_info_t pnt_info;
945 int out_len, dump_size, offset;
946
947 if (slot_p == NULL) {
948 prev_file = NULL;
949 prev_line = 0;
950 user_size = 0;
951 start_user = user_pnt;
952 }
953 else {
954 prev_file = slot_p->sa_file;
955 prev_line = slot_p->sa_line;
956 user_size = slot_p->sa_user_size;
957 if (user_pnt == NULL) {
958 get_pnt_info(slot_p, &pnt_info);
959 start_user = pnt_info.pi_user_start;
960 }
961 else {
962 start_user = user_pnt;
963 }
964 }
965
966 /* get a proper reason string */
967 if (reason != NULL) {
968 dmalloc_message(" error details: %s", reason);
969 }
970
971 /* dump the pointer information */
972 if (start_user == NULL) {
973 dmalloc_message(" from '%s' prev access '%s'",
974 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
975 now_file, now_line),
976 _dmalloc_chunk_desc_pnt(where_buf2, sizeof(where_buf2),
977 prev_file, prev_line));
978 }
979 else {
980 dmalloc_message(" pointer '%p' from '%s' prev access '%s'",
981 start_user,
982 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
983 now_file, now_line),
984 _dmalloc_chunk_desc_pnt(where_buf2, sizeof(where_buf2),
985 prev_file, prev_line));
986 }
987
988 /*
989 * If we aren't logging bad space or we didn't error with an
990 * overwrite error then don't log the bad bytes.
991 */
992 if ((! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_BAD_SPACE))
993 || (dmalloc_errno != DMALLOC_ERROR_UNDER_FENCE
994 && dmalloc_errno != DMALLOC_ERROR_OVER_FENCE
995 && dmalloc_errno != DMALLOC_ERROR_FREE_OVERWRITTEN)) {
996 /* we call the error function after writing more info to the logfile */
997 dmalloc_error(where);
998 return;
999 }
1000
1001 /* NOTE: display memory like this has the potential for generating a core */
1002 if (dmalloc_errno == DMALLOC_ERROR_UNDER_FENCE) {
1003 /* NOTE: only dump out the proper fence-post area once */
1004 if (! dump_bottom_b) {
1005 out_len = expand_chars(fence_bottom, FENCE_BOTTOM_SIZE, out,
1006 sizeof(out));
1007 dmalloc_message(" dump of proper fence-bottom bytes: '%.*s'",
1008 out_len, out);
1009 dump_bottom_b = 1;
1010 }
1011 offset = -FENCE_BOTTOM_SIZE;
1012 dump_size = DUMP_SPACE + FENCE_BOTTOM_SIZE;
1013 if (dump_size > user_size + FENCE_OVERHEAD_SIZE) {
1014 dump_size = user_size + FENCE_OVERHEAD_SIZE;
1015 }
1016 }
1017 else if (dmalloc_errno == DMALLOC_ERROR_OVER_FENCE
1018 && user_size > 0) {
1019 /* NOTE: only dump out the proper fence-post area once */
1020 if (! dump_top_b) {
1021 out_len = expand_chars(fence_top, FENCE_TOP_SIZE, out, sizeof(out));
1022 dmalloc_message(" dump of proper fence-top bytes: '%.*s'",
1023 out_len, out);
1024 dump_top_b = 1;
1025 }
1026 /*
1027 * The size includes the bottom fence post area. We want it to
1028 * align with the start of the top fence post area.
1029 */
1030 if (DUMP_SPACE > user_size + FENCE_OVERHEAD_SIZE) {
1031 dump_size = user_size + FENCE_OVERHEAD_SIZE;
1032 offset = -FENCE_BOTTOM_SIZE;
1033 }
1034 else {
1035 dump_size = DUMP_SPACE;
1036 /* we will go backwards possibly up to FENCE_BOTTOM_SIZE offset */
1037 offset = user_size + FENCE_TOP_SIZE - DUMP_SPACE;
1038 }
1039 }
1040 else {
1041 dump_size = DUMP_SPACE;
1042 offset = 0;
1043 if (user_size > 0 && dump_size > user_size) {
1044 dump_size = user_size;
1045 }
1046 }
1047
1048 dump_pnt = (char *)start_user + offset;
1049 if (IS_IN_HEAP(dump_pnt)) {
1050 out_len = expand_chars(dump_pnt, dump_size, out, sizeof(out));
1051 dmalloc_message(" dump of '%p'%+d: '%.*s'",
1052 start_user, offset, out_len, out);
1053 }
1054 else {
1055 dmalloc_message(" dump of '%p'%+d failed: not in heap", start_user, offset);
1056 }
1057
1058 /* find the previous pointer in case it ran over */
1059 if (dmalloc_errno == DMALLOC_ERROR_UNDER_FENCE && start_user != NULL) {
1060 other_p = find_address((char *)start_user - FENCE_BOTTOM_SIZE - 1,
1061 0 /* used list */, 1 /* not exact pointer */,
1062 skip_update);
1063 if (other_p != NULL) {
1064 dmalloc_message(" prev pointer '%p' (size %u) may have run over from '%s'",
1065 other_p->sa_mem, other_p->sa_user_size,
1066 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
1067 other_p->sa_file,
1068 other_p->sa_line));
1069 }
1070 }
1071 /* find the next pointer in case it ran under */
1072 else if (dmalloc_errno == DMALLOC_ERROR_OVER_FENCE
1073 && start_user != NULL
1074 && slot_p != NULL) {
1075 other_p = find_address((char *)slot_p->sa_mem + slot_p->sa_total_size,
1076 0 /* used list */, 1 /* not exact pointer */,
1077 skip_update);
1078 if (other_p != NULL) {
1079 dmalloc_message(" next pointer '%p' (size %u) may have run under from '%s'",
1080 other_p->sa_mem, other_p->sa_user_size,
1081 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
1082 other_p->sa_file,
1083 other_p->sa_line));
1084 }
1085 }
1086
1087 /* we call the error function after writing more info to the logfile */
1088 dmalloc_error(where);
1089 }
1090
1091 /*
1092 * static int fence_read
1093 *
1094 * Check a pointer for fence-post magic numbers.
1095 *
1096 * Returns 1 if the fence posts are good or 0 if they are not.
1097 *
1098 * ARGUMENTS:
1099 *
1100 * info_p -> Pointer information that we are checking.
1101 */
fence_read(const pnt_info_t * info_p)1102 static int fence_read(const pnt_info_t *info_p)
1103 {
1104 /* check magic numbers in bottom of allocation block */
1105 if (memcmp(fence_bottom, info_p->pi_fence_bottom, FENCE_BOTTOM_SIZE) != 0) {
1106 dmalloc_errno = DMALLOC_ERROR_UNDER_FENCE;
1107 return 0;
1108 }
1109
1110 /* check numbers at top of allocation block */
1111 if (memcmp(fence_top, info_p->pi_fence_top, FENCE_TOP_SIZE) != 0) {
1112 dmalloc_errno = DMALLOC_ERROR_OVER_FENCE;
1113 return 0;
1114 }
1115
1116 return 1;
1117 }
1118
1119 /*
1120 * static void clear_alloc
1121 *
1122 * Setup allocations by writing fence post and doing any necessary
1123 * clearing of memory.
1124 *
1125 * Returns 1 if the fence posts are good or 0 if they are not.
1126 *
1127 * ARGUMENTS:
1128 *
1129 * slot_p <-> Slot we are clearing.
1130 *
1131 * info_p -> Pointer to information about the allocation.
1132 *
1133 * old_size -> If there was an old-size that we have copied into the
1134 * new pointer then set this. If 0 then it will clear the entire
1135 * allocation.
1136 *
1137 * func_id -> ID of the function which is doing the allocation. Used
1138 * to determine if we should 0 memory for [re]calloc.
1139 */
clear_alloc(skip_alloc_t * slot_p,pnt_info_t * info_p,const unsigned int old_size,const int func_id)1140 static void clear_alloc(skip_alloc_t *slot_p, pnt_info_t *info_p,
1141 const unsigned int old_size, const int func_id)
1142 {
1143 char *start_p;
1144 int num;
1145
1146 /*
1147 * NOTE: The alloc blank flag is set so we blank a slot when it is
1148 * allocated. It used to be that the allocated spaces were blanked
1149 * and the free spaces of the allocated chunk were blanked only if
1150 * the FREE_BLANK flag was enabled. Wrong!
1151 */
1152
1153 /*
1154 * Set our slot blank flag if the flags are set now. This will
1155 * carry over with a realloc.
1156 */
1157 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_ALLOC_BLANK)
1158 || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_BLANK)) {
1159 BIT_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK);
1160 }
1161
1162 /*
1163 * If we have a fence post protected valloc then there is almost a
1164 * full block at the front what is "free". Set it with blank chars.
1165 */
1166 if (info_p->pi_fence_b) {
1167 num = (char *)info_p->pi_fence_bottom - (char *)info_p->pi_alloc_start;
1168 /* alloc-blank NOT free-blank */
1169 if (num > 0 && BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK)) {
1170 memset(info_p->pi_alloc_start, ALLOC_BLANK_CHAR, num);
1171 }
1172 }
1173
1174 /*
1175 * If we are allocating or extending memory, write in our alloc
1176 * chars.
1177 */
1178 start_p = (char *)info_p->pi_user_start + old_size;
1179
1180 num = (char *)info_p->pi_user_bounds - start_p;
1181 if (num > 0) {
1182 if (func_id == DMALLOC_FUNC_CALLOC || func_id == DMALLOC_FUNC_RECALLOC) {
1183 memset(start_p, 0, num);
1184 }
1185 else if (BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK)) {
1186 memset(start_p, ALLOC_BLANK_CHAR, num);
1187 }
1188 }
1189
1190 /* write in fence-post info */
1191 if (info_p->pi_fence_b) {
1192 memcpy(info_p->pi_fence_bottom, fence_bottom, FENCE_BOTTOM_SIZE);
1193 memcpy(info_p->pi_fence_top, fence_top, FENCE_TOP_SIZE);
1194 }
1195
1196 /*
1197 * Now clear the rest of the block above any fence post space with
1198 * free characters.
1199 *
1200 * NOTE alloc-blank NOT free-blank
1201 */
1202 if (BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK)) {
1203
1204 if (info_p->pi_fence_b) {
1205 start_p = (char *)info_p->pi_fence_top + FENCE_TOP_SIZE;
1206 }
1207 else {
1208 start_p = info_p->pi_user_bounds;
1209 }
1210
1211 num = (char *)info_p->pi_alloc_bounds - start_p;
1212 if (num > 0) {
1213 memset(start_p, ALLOC_BLANK_CHAR, num);
1214 }
1215 }
1216 }
1217
1218 /************************** administration functions *************************/
1219
1220 /*
1221 * static int create_divided_chunks
1222 *
1223 * Get a divided-block from the free list or heap allocation.
1224 *
1225 * Returns 1 on success or 0 on failure.
1226 *
1227 * ARGUMENTS:
1228 *
1229 * div_size -> Size of the divided block that we are allocating.
1230 */
create_divided_chunks(const unsigned int div_size)1231 static int create_divided_chunks(const unsigned int div_size)
1232 {
1233 void *mem, *bounds_p;
1234
1235 /* allocate a 1 block chunk that we will cut up into pieces */
1236 mem = _dmalloc_heap_alloc(BLOCK_SIZE);
1237 if (mem == HEAP_ALLOC_ERROR) {
1238 /* error code set in _dmalloc_heap_alloc */
1239 return 0;
1240 }
1241 user_block_c++;
1242
1243 /*
1244 * now run through the block and add the the locations to the
1245 * free-list
1246 */
1247
1248 /* end of the block */
1249 bounds_p = (char *)mem + BLOCK_SIZE - div_size;
1250
1251 for (; mem <= bounds_p; mem = (char *)mem + div_size) {
1252 /* insert the rest of the blocks into the free-list */
1253 if (insert_address(mem, 1 /* free list */, div_size) == NULL) {
1254 /* error set in insert_address */
1255 return 0;
1256 }
1257 free_space_bytes += div_size;
1258 }
1259
1260 return 1;
1261 }
1262
1263 /*
1264 * static skip_alloc_t *use_free_memory
1265 *
1266 * Find a free memory chunk and remove it from the free list and put
1267 * it on the used list if available.
1268 *
1269 * Returns a valid slot pointer on sucess or NULL on failure.
1270 *
1271 * ARGUMENTS:
1272 *
1273 * size -> Size of the block that we are looking for.
1274 *
1275 * update_p -> Pointer to the skip_alloc entry we are using to hold
1276 * the update pointers.
1277 */
use_free_memory(const unsigned int size,skip_alloc_t * update_p)1278 static skip_alloc_t *use_free_memory(const unsigned int size,
1279 skip_alloc_t *update_p)
1280 {
1281 skip_alloc_t *slot_p;
1282
1283 #if FREED_POINTER_DELAY
1284 /*
1285 * check the free wait list to see if any of the waiting pointers
1286 * need to be moved off and inserted into the free list
1287 */
1288 for (slot_p = free_wait_list_head; slot_p != NULL; ) {
1289 skip_alloc_t *next_p;
1290
1291 /* we are done if we find a pointer delay in the future */
1292 if (slot_p->sa_use_iter + FREED_POINTER_DELAY > _dmalloc_iter_c) {
1293 break;
1294 }
1295
1296 /* put slot on free list */
1297 next_p = slot_p->sa_next_p[0];
1298 if (! insert_slot(slot_p, 1 /* free list */)) {
1299 /* error dumped in insert_slot */
1300 return NULL;
1301 }
1302
1303 /* adjust our linked list */
1304 slot_p = next_p;
1305 free_wait_list_head = slot_p;
1306 if (slot_p == NULL) {
1307 free_wait_list_tail = NULL;
1308 }
1309 }
1310 #endif
1311
1312 /* find a free block which matches the size */
1313 slot_p = find_free_size(size, update_p);
1314 if (slot_p == NULL) {
1315 return NULL;
1316 }
1317
1318 /* sanity check */
1319 if (slot_p->sa_total_size != size) {
1320 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
1321 dmalloc_error("use_free_memory");
1322 return NULL;
1323 }
1324
1325 /* remove from free list */
1326 if (! remove_slot(slot_p, update_p)) {
1327 /* error reported in remove_slot */
1328 return NULL;
1329 }
1330
1331 /* set to user allocated space */
1332 slot_p->sa_flags = ALLOC_FLAG_USER;
1333
1334 /* insert it into our address list */
1335 if (! insert_slot(slot_p, 0 /* used list */)) {
1336 /* error set in insert_slot */
1337 return NULL;
1338 }
1339
1340 free_space_bytes -= slot_p->sa_total_size;
1341
1342 return slot_p;
1343 }
1344
1345 /*
1346 * static skip_alloc_t *get_divided_memory
1347 *
1348 * Get a divided memory block from the free list or heap allocation.
1349 *
1350 * Returns a valid skip slot pointer on success or NULL on failure.
1351 *
1352 * ARGUMENTS:
1353 *
1354 * size -> Size of the block that we are allocating.
1355 */
get_divided_memory(const unsigned int size)1356 static skip_alloc_t *get_divided_memory(const unsigned int size)
1357 {
1358 skip_alloc_t *slot_p;
1359 unsigned int need_size;
1360 int *bits_p;
1361
1362 for (bits_p = bit_sizes;; bits_p++) {
1363 if (*bits_p >= size) {
1364 break;
1365 }
1366 }
1367 need_size = *bits_p;
1368
1369 /* find a free block which matches the size */
1370 slot_p = use_free_memory(need_size, skip_update);
1371 if (slot_p != NULL) {
1372 return slot_p;
1373 }
1374
1375 /* need to get more slots */
1376 if (! create_divided_chunks(need_size)) {
1377 /* errors dumped in create_divided_chunks */
1378 return NULL;
1379 }
1380
1381 /* now we ask again for the free memory */
1382 slot_p = use_free_memory(need_size, skip_update);
1383 if (slot_p == NULL) {
1384 /* huh? This isn't right. */
1385 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
1386 dmalloc_error("get_divided_memory");
1387 return NULL;
1388 }
1389
1390 return slot_p;
1391 }
1392
1393 /*
1394 * static skip_alloc_t *get_memory
1395 *
1396 * Get a block from the free list or heap allocation.
1397 *
1398 * Returns a valid skip slot pointer on success or NULL on failure.
1399 *
1400 * ARGUMENTS:
1401 *
1402 * size -> Size of the block that we are allocating.
1403 */
get_memory(const unsigned int size)1404 static skip_alloc_t *get_memory(const unsigned int size)
1405 {
1406 skip_alloc_t *slot_p, *update_p;
1407 void *mem;
1408 unsigned int need_size, block_n;
1409
1410 /* do we need to print admin info? */
1411 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_ADMIN)) {
1412 dmalloc_message("need %d bytes", size);
1413 }
1414
1415 /* will this allocate put us over the limit? */
1416 if (_dmalloc_memory_limit > 0
1417 && alloc_cur_given + size > _dmalloc_memory_limit) {
1418 dmalloc_errno = DMALLOC_ERROR_OVER_LIMIT;
1419 dmalloc_error("get_memory");
1420 return NULL;
1421 }
1422
1423 /* do we have a divided block here? */
1424 if (size <= BLOCK_SIZE / 2) {
1425 return get_divided_memory(size);
1426 }
1427
1428 /* round up to the nearest block size */
1429 need_size = size + BLOCK_SIZE - 1;
1430 block_n = need_size / BLOCK_SIZE;
1431 need_size = block_n * BLOCK_SIZE;
1432
1433 update_p = skip_update;
1434
1435 /* find a free block which matches the size */
1436 slot_p = use_free_memory(need_size, update_p);
1437 if (slot_p != NULL) {
1438 return slot_p;
1439 }
1440
1441 /* if there are blocks that are larger than this */
1442 slot_p = update_p->sa_next_p[0];
1443 if (slot_p != NULL && slot_p->sa_total_size > size) {
1444
1445 /*
1446 * now we ask again for the memory because we need to reset the
1447 * update pointer list
1448 */
1449 slot_p = use_free_memory(need_size, update_p);
1450 if (slot_p != NULL) {
1451 /* huh? This isn't right. */
1452 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
1453 dmalloc_error("get_memory");
1454 return NULL;
1455 }
1456 }
1457
1458 /* allocate the memory necessary for the new blocks */
1459 mem = _dmalloc_heap_alloc(need_size);
1460 if (mem == HEAP_ALLOC_ERROR) {
1461 /* error code set in _dmalloc_heap_alloc */
1462 return NULL;
1463 }
1464 user_block_c += block_n;
1465
1466 /* create our slot */
1467 slot_p = insert_address(mem, 0 /* used list */, need_size);
1468 if (slot_p == NULL) {
1469 /* error set in insert_address */
1470 return NULL;
1471 }
1472
1473 return slot_p;
1474 }
1475
1476 /*
1477 * static int check_used_slot
1478 *
1479 * Check out the pointer in a allocated slot to make sure it is good.
1480 *
1481 * Returns 1 on success or 0 on failure.
1482 *
1483 * ARGUMENTS:
1484 *
1485 * slot_p -> Slot that we are checking.
1486 *
1487 * user_pnt -> User pointer which was used to get the slot or NULL.
1488 *
1489 * exact_b -> Set to 1 to find the pointer specifically. Otherwise we
1490 * can find the pointer inside of an allocation.
1491 *
1492 * strlen_b -> Make sure that pnt can hold at least a strlen + 1
1493 * bytes. If 0 then ignore.
1494 *
1495 * min_size -> Make sure that pnt can hold at least that many bytes.
1496 * If 0 then ignore.
1497 */
check_used_slot(const skip_alloc_t * slot_p,const void * user_pnt,const int exact_b,const int strlen_b,const int min_size)1498 static int check_used_slot(const skip_alloc_t *slot_p,
1499 const void *user_pnt, const int exact_b,
1500 const int strlen_b, const int min_size)
1501 {
1502 const char *file, *name_p, *bounds_p, *mem_p;
1503 unsigned int line, num;
1504 pnt_info_t pnt_info;
1505
1506 if (! (BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_USER)
1507 || BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_EXTERN)
1508 || BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_ADMIN))) {
1509 dmalloc_errno = DMALLOC_ERROR_SLOT_CORRUPT;
1510 return 0;
1511 }
1512
1513 /* get pointer info */
1514 get_pnt_info(slot_p, &pnt_info);
1515
1516 /* the user pointer needs to be within the user space */
1517 if (user_pnt != NULL && (char *)user_pnt < (char *)pnt_info.pi_user_start) {
1518 dmalloc_errno = DMALLOC_ERROR_WOULD_OVERWRITE;
1519 return 0;
1520 }
1521
1522 /* if we need the exact pointer, make sure that the user_pnt agrees */
1523 if (exact_b && user_pnt != pnt_info.pi_user_start) {
1524 dmalloc_errno = DMALLOC_ERROR_NOT_START_BLOCK;
1525 return 0;
1526 }
1527
1528 #if LARGEST_ALLOCATION
1529 /* have we exceeded the upper bounds */
1530 if (slot_p->sa_user_size > LARGEST_ALLOCATION) {
1531 dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
1532 return 0;
1533 }
1534 #endif
1535
1536 /* check our total block size */
1537 if (slot_p->sa_total_size > BLOCK_SIZE / 2
1538 && slot_p->sa_total_size % BLOCK_SIZE != 0) {
1539 dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
1540 return 0;
1541 }
1542
1543 /*
1544 * If we have a valloc allocation then the _user_ pnt should be
1545 * block aligned otherwise the chunk_pnt should be.
1546 */
1547 if (pnt_info.pi_valloc_b) {
1548
1549 if ((PNT_ARITH_TYPE)pnt_info.pi_user_start % BLOCK_SIZE != 0) {
1550 dmalloc_errno = DMALLOC_ERROR_NOT_ON_BLOCK;
1551 return 0;
1552 }
1553 if (slot_p->sa_total_size < BLOCK_SIZE) {
1554 dmalloc_errno = DMALLOC_ERROR_SLOT_CORRUPT;
1555 return 0;
1556 }
1557
1558 /* now check the below space to make sure it is still clear */
1559 if (pnt_info.pi_fence_b && pnt_info.pi_blanked_b) {
1560 num = (char *)pnt_info.pi_fence_bottom - (char *)pnt_info.pi_alloc_start;
1561 if (num > 0) {
1562 for (mem_p = pnt_info.pi_alloc_start;
1563 mem_p < (char *)pnt_info.pi_fence_bottom;
1564 mem_p++) {
1565 if (*mem_p != ALLOC_BLANK_CHAR) {
1566 dmalloc_errno = DMALLOC_ERROR_FREE_OVERWRITTEN;
1567 return 0;
1568 }
1569 }
1570 }
1571 }
1572 }
1573
1574 /* check out the fence-posts */
1575 if (pnt_info.pi_fence_b && (! fence_read(&pnt_info))) {
1576 /* errno set in fence_read */
1577 return 0;
1578 }
1579
1580 /* check above the allocation to see if it's been overwritten */
1581 if (pnt_info.pi_blanked_b) {
1582
1583 if (pnt_info.pi_fence_b) {
1584 mem_p = (char *)pnt_info.pi_fence_top + FENCE_TOP_SIZE;
1585 }
1586 else {
1587 mem_p = pnt_info.pi_user_bounds;
1588 }
1589
1590 for (; mem_p < (char *)pnt_info.pi_alloc_bounds; mem_p++) {
1591 if (*mem_p != ALLOC_BLANK_CHAR) {
1592 dmalloc_errno = DMALLOC_ERROR_FREE_OVERWRITTEN;
1593 return 0;
1594 }
1595 }
1596 }
1597
1598 file = slot_p->sa_file;
1599 line = slot_p->sa_line;
1600
1601 /* check line number */
1602 #if MAX_LINE_NUMBER
1603 if (line > MAX_LINE_NUMBER) {
1604 dmalloc_errno = DMALLOC_ERROR_BAD_LINE;
1605 return 0;
1606 }
1607 #endif
1608
1609 /*
1610 * Check file pointer only if file is not NULL and line is not 0
1611 * which implies that file is a return-addr.
1612 */
1613 #if MAX_FILE_LENGTH
1614 if (file != DMALLOC_DEFAULT_FILE && line != DMALLOC_DEFAULT_LINE) {
1615 /* NOTE: we don't use strlen here because we might check too far */
1616 bounds_p = file + MAX_FILE_LENGTH;
1617 for (name_p = file; name_p <= bounds_p && *name_p != '\0'; name_p++) {
1618 }
1619 if (name_p > bounds_p
1620 || name_p < file + MIN_FILE_LENGTH) {
1621 dmalloc_errno = DMALLOC_ERROR_BAD_FILE;
1622 return 0;
1623 }
1624 }
1625 #endif
1626
1627 #if LOG_PNT_SEEN_COUNT
1628 /*
1629 * We divide by 2 here because realloc which returns the same
1630 * pointer will seen_c += 2. However, it will never be more than
1631 * twice the iteration value. We divide by two to not overflow
1632 * iter_c * 2.
1633 */
1634 if (slot_p->sa_seen_c / 2 > _dmalloc_iter_c) {
1635 dmalloc_errno = DMALLOC_ERROR_SLOT_CORRUPT;
1636 return 0;
1637 }
1638 #endif
1639
1640 if (strlen_b) {
1641 int equals_okay_b = 0;
1642 mem_p = (char *)user_pnt;
1643 if (min_size > 0) {
1644 bounds_p = mem_p + min_size;
1645 /* min_size can be out of bounds as long as we find a \0 beforehand */
1646 if (bounds_p > (char *)pnt_info.pi_user_bounds) {
1647 bounds_p = (char *)pnt_info.pi_user_bounds;
1648 } else {
1649 /* we can equals our boundary if our min_size <= user_bounds */
1650 equals_okay_b = 1;
1651 }
1652 } else {
1653 bounds_p = (char *)pnt_info.pi_user_bounds;
1654 }
1655 for (; mem_p < bounds_p; mem_p++) {
1656 if (*mem_p == '\0') {
1657 break;
1658 }
1659 }
1660 /* mem_p can == bounds_p (if equals-ok) if we hit the min_size but can't >= user_bounds */
1661 if (mem_p > (char *)pnt_info.pi_user_bounds
1662 || ((! equals_okay_b) && mem_p == (char *)pnt_info.pi_user_bounds)) {
1663 dmalloc_errno = DMALLOC_ERROR_WOULD_OVERWRITE;
1664 return 0;
1665 }
1666 } else if (min_size > 0) {
1667 if ((char *)user_pnt + min_size > (char *)pnt_info.pi_user_bounds) {
1668 dmalloc_errno = DMALLOC_ERROR_WOULD_OVERWRITE;
1669 return 0;
1670 }
1671 }
1672
1673 return 1;
1674 }
1675
1676 /*
1677 * static int check_free_slot
1678 *
1679 * Check out the pointer in a slot to make sure it is good.
1680 *
1681 * Returns 1 on success or 0 on failure.
1682 *
1683 * ARGUMENTS:
1684 *
1685 * slot_p -> Slot that we are checking.
1686 */
check_free_slot(const skip_alloc_t * slot_p)1687 static int check_free_slot(const skip_alloc_t *slot_p)
1688 {
1689 char *check_p;
1690
1691 if (! BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FREE)) {
1692 dmalloc_errno = DMALLOC_ERROR_SLOT_CORRUPT;
1693 return 0;
1694 }
1695
1696 if (BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK)) {
1697 for (check_p = (char *)slot_p->sa_mem;
1698 check_p < (char *)slot_p->sa_mem + slot_p->sa_total_size;
1699 check_p++) {
1700 if (*check_p != FREE_BLANK_CHAR) {
1701 dmalloc_errno = DMALLOC_ERROR_FREE_OVERWRITTEN;
1702 return 0;
1703 }
1704 }
1705 }
1706
1707 #if LOG_PNT_SEEN_COUNT
1708 /*
1709 * We divide by 2 here because realloc which returns the same
1710 * pointer will seen_c += 2. However, it will never be more than
1711 * twice the iteration value. We divide by two to not overflow
1712 * iter_c * 2.
1713 */
1714 if (slot_p->sa_seen_c / 2 > _dmalloc_iter_c) {
1715 dmalloc_errno = DMALLOC_ERROR_SLOT_CORRUPT;
1716 return 0;
1717 }
1718 #endif
1719
1720 return 1;
1721 }
1722
1723 /***************************** exported routines *****************************/
1724
1725 /*
1726 * int _dmalloc_chunk_startup
1727 *
1728 * Startup the low level malloc routines.
1729 *
1730 * Returns 1 on success or 0 on failure.
1731 */
_dmalloc_chunk_startup(void)1732 int _dmalloc_chunk_startup(void)
1733 {
1734 unsigned int value;
1735 char *pos_p, *max_p;
1736 int bit_c, *bits_p;
1737
1738 value = FENCE_MAGIC_BOTTOM;
1739 max_p = fence_bottom + FENCE_BOTTOM_SIZE;
1740 for (pos_p = fence_bottom;
1741 pos_p < max_p;
1742 pos_p += sizeof(value)) {
1743 if (pos_p + sizeof(value) <= max_p) {
1744 memcpy(pos_p, (char *)&value, sizeof(value));
1745 }
1746 else {
1747 memcpy(pos_p, (char *)&value, max_p - pos_p);
1748 }
1749 }
1750
1751 value = FENCE_MAGIC_TOP;
1752 max_p = fence_top + FENCE_TOP_SIZE;
1753 for (pos_p = fence_top; pos_p < max_p; pos_p += sizeof(value)) {
1754 if (pos_p + sizeof(value) <= max_p) {
1755 memcpy(pos_p, (char *)&value, sizeof(value));
1756 }
1757 else {
1758 memcpy(pos_p, (char *)&value, max_p - pos_p);
1759 }
1760 }
1761
1762 /* initialize the bits array */
1763 bits_p = bit_sizes;
1764 for (bit_c = 0; bit_c < BASIC_BLOCK; bit_c++) {
1765 if ((1 << bit_c) >= CHUNK_SMALLEST_BLOCK) {
1766 *bits_p++ = 1 << bit_c;
1767 }
1768 }
1769
1770 /* set the admin flags on the two statically allocated slots */
1771 skip_free_list->sa_flags = ALLOC_FLAG_ADMIN;
1772 skip_address_list->sa_flags = ALLOC_FLAG_ADMIN;
1773
1774 _dmalloc_table_init(&mem_table_alloc, mem_table_alloc_entries,
1775 sizeof(mem_table_alloc_entries) /
1776 sizeof(*mem_table_alloc_entries));
1777 _dmalloc_table_init(&mem_table_changed, mem_table_changed_entries,
1778 sizeof(mem_table_changed_entries) /
1779 sizeof(*mem_table_changed_entries));
1780
1781 return 1;
1782 }
1783
1784 /*
1785 * char *_dmalloc_chunk_desc_pnt
1786 *
1787 * Write into a buffer a pointer description with file and line-number.
1788 *
1789 * Returns a pointer to buffer 1st argument.
1790 *
1791 * ARGUMENTS:
1792 *
1793 * buf <-> Passed in buffer which will be filled with a description of
1794 * the pointer.
1795 *
1796 * buf_size -> Size of the buffer in bytes.
1797 *
1798 * file -> File name, return address, or NULL.
1799 *
1800 * line -> Line number or 0.
1801 */
_dmalloc_chunk_desc_pnt(char * buf,const int buf_size,const char * file,const unsigned int line)1802 char *_dmalloc_chunk_desc_pnt(char *buf, const int buf_size,
1803 const char *file, const unsigned int line)
1804 {
1805 if (file == DMALLOC_DEFAULT_FILE && line == DMALLOC_DEFAULT_LINE) {
1806 (void)loc_snprintf(buf, buf_size, "unknown");
1807 }
1808 else if (line == DMALLOC_DEFAULT_LINE) {
1809 (void)loc_snprintf(buf, buf_size, "ra=%p", file);
1810 }
1811 else if (file == DMALLOC_DEFAULT_FILE) {
1812 (void)loc_snprintf(buf, buf_size, "ra=ERROR(line=%u)", line);
1813 }
1814 else {
1815 (void)loc_snprintf(buf, buf_size, "%.*s:%u", MAX_FILE_LENGTH, file, line);
1816 }
1817
1818 return buf;
1819 }
1820
1821 /*
1822 * int _dmalloc_chunk_read_info
1823 *
1824 * Return some information associated with a pointer.
1825 *
1826 * Returns 1 if the pointer is okay or 0 if a problem with pointer
1827 *
1828 * ARGUMENTS:
1829 *
1830 * user_pnt -> Pointer we are checking.
1831 *
1832 * where <- Where the check is being made from.
1833 *
1834 * user_size_p <- Pointer to an unsigned int which, if not NULL, will
1835 * be set to the size of bytes that the user requested.
1836 *
1837 * alloc_size_p <- Pointer to an unsigned int which, if not NULL, will
1838 * be set to the total given size of bytes including block overhead.
1839 *
1840 * file_p <- Pointer to a character pointer which, if not NULL, will
1841 * be set to the file where the pointer was allocated.
1842 *
1843 * line_p <- Pointer to a character pointer which, if not NULL, will
1844 * be set to the line-number where the pointer was allocated.
1845 *
1846 * ret_attr_p <- Pointer to a void pointer, if not NULL, will be set
1847 * to the return-address where the pointer was allocated.
1848 *
1849 * seen_cp <- Pointer to an unsigned long which, if not NULL, will be
1850 * set to the number of times the pointer has been "seen".
1851 *
1852 * used_p <- Pointer to an unsigned long which, if not NULL, will be
1853 * set to the last time the pointer was "used".
1854 *
1855 * valloc_bp <- Pointer to an integer which, if not NULL, will be set
1856 * to 1 if the pointer was allocated with valloc otherwise 0.
1857 *
1858 * fence_bp <- Pointer to an integer which, if not NULL, will be set
1859 * to 1 if the pointer has the fence bit set otherwise 0.
1860 */
_dmalloc_chunk_read_info(const void * user_pnt,const char * where,unsigned int * user_size_p,unsigned int * alloc_size_p,char ** file_p,unsigned int * line_p,void ** ret_attr_p,unsigned long ** seen_cp,unsigned long * used_p,int * valloc_bp,int * fence_bp)1861 int _dmalloc_chunk_read_info(const void *user_pnt, const char *where,
1862 unsigned int *user_size_p,
1863 unsigned int *alloc_size_p, char **file_p,
1864 unsigned int *line_p, void **ret_attr_p,
1865 unsigned long **seen_cp,
1866 unsigned long *used_p, int *valloc_bp,
1867 int *fence_bp)
1868 {
1869 skip_alloc_t *slot_p;
1870
1871 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
1872 dmalloc_message("reading info about pointer '%p'", user_pnt);
1873 }
1874
1875 /* find the pointer with loose checking for fence */
1876 slot_p = find_address(user_pnt, 0 /* used list */, 0 /* not exact pointer */,
1877 skip_update);
1878 if (slot_p == NULL) {
1879 dmalloc_errno = DMALLOC_ERROR_NOT_FOUND;
1880 log_error_info(NULL, 0, user_pnt, NULL, "finding address in heap", where);
1881 return 0;
1882 }
1883
1884 /* might as well check the pointer now */
1885 if (! check_used_slot(slot_p, user_pnt, 1 /* exact */, 0 /* no strlen */,
1886 0 /* no min-size */)) {
1887 /* errno set in check_slot */
1888 log_error_info(NULL, 0, user_pnt, slot_p, "checking pointer admin", where);
1889 return 0;
1890 }
1891
1892 /* write info back to user space */
1893 SET_POINTER(user_size_p, slot_p->sa_user_size);
1894 SET_POINTER(alloc_size_p, slot_p->sa_total_size);
1895 if (slot_p->sa_file == DMALLOC_DEFAULT_FILE) {
1896 SET_POINTER(file_p, NULL);
1897 }
1898 else {
1899 SET_POINTER(file_p, (char *)slot_p->sa_file);
1900 }
1901 SET_POINTER(line_p, slot_p->sa_line);
1902 /* if the line is blank then the file will be 0 or the return address */
1903 if (slot_p->sa_line == DMALLOC_DEFAULT_LINE) {
1904 SET_POINTER(ret_attr_p, (char *)slot_p->sa_file);
1905 }
1906 else {
1907 SET_POINTER(ret_attr_p, NULL);
1908 }
1909 #if LOG_PNT_SEEN_COUNT
1910 SET_POINTER(seen_cp, &slot_p->sa_seen_c);
1911 #else
1912 SET_POINTER(seen_cp, NULL);
1913 #endif
1914 SET_POINTER(used_p, slot_p->sa_use_iter);
1915 SET_POINTER(valloc_bp, BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_VALLOC));
1916 SET_POINTER(fence_bp, BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FENCE));
1917
1918 return 1;
1919 }
1920
1921 /******************************* heap checking *******************************/
1922
1923 /*
1924 * int _dmalloc_chunk_heap_check
1925 *
1926 * Run extensive tests on the entire heap.
1927 *
1928 * Returns 1 if the heap is okay or 0 if a problem was detected
1929 */
_dmalloc_chunk_heap_check(void)1930 int _dmalloc_chunk_heap_check(void)
1931 {
1932 skip_alloc_t *slot_p;
1933 entry_block_t *block_p;
1934 int ret, level_c, checking_list_c = 0;
1935 int final = 1;
1936
1937 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
1938 dmalloc_message("checking heap");
1939 }
1940
1941 heap_check_c++;
1942
1943 /*
1944 * first, run through all of the admin structures and check for
1945 * validity
1946 */
1947 for (level_c = 0; level_c < MAX_SKIP_LEVEL; level_c++) {
1948 unsigned int *magic3_p, magic3;
1949
1950 /* run through the blocks and test them */
1951 for (block_p = entry_blocks[level_c];
1952 block_p != NULL;
1953 block_p = block_p->eb_next_p) {
1954
1955 /* better be in the heap */
1956 if (! IS_IN_HEAP(block_p)) {
1957 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
1958 dmalloc_error("_dmalloc_chunk_heap_check");
1959 return 0;
1960 }
1961
1962 /* get the magic3 at the end of the block */
1963 magic3_p = (unsigned int *)((char *)block_p + BLOCK_SIZE -
1964 sizeof(*magic3_p));
1965 memcpy(&magic3, magic3_p, sizeof(magic3));
1966
1967 /* check magics */
1968 if (block_p->eb_magic1 != ENTRY_BLOCK_MAGIC1
1969 || block_p->eb_magic2 != ENTRY_BLOCK_MAGIC2
1970 || magic3 != ENTRY_BLOCK_MAGIC3) {
1971 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
1972 dmalloc_error("_dmalloc_chunk_heap_check");
1973 return 0;
1974 }
1975
1976 /* check for a valid level */
1977 if (block_p->eb_level_n != level_c) {
1978 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
1979 dmalloc_error("_dmalloc_chunk_heap_check");
1980 return 0;
1981 }
1982
1983 /* now we look up the block and make sure it exists and is valid */
1984 slot_p = find_address(block_p, 0 /* used list */, 1 /* exact */,
1985 skip_update);
1986 if (slot_p == NULL) {
1987 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
1988 dmalloc_error("_dmalloc_chunk_heap_check");
1989 return 0;
1990 }
1991 if ((! BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_ADMIN))
1992 || slot_p->sa_mem != block_p
1993 || slot_p->sa_total_size != BLOCK_SIZE
1994 || slot_p->sa_level_n != level_c) {
1995 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
1996 dmalloc_error("_dmalloc_chunk_heap_check");
1997 return 0;
1998 }
1999
2000 /*
2001 * NOTE: we could now check each of the entries in the block to
2002 * make sure that they are valid and on the used or free list
2003 */
2004 }
2005 }
2006
2007 /*
2008 * Now run through the used pointers and check each one.
2009 */
2010 for (slot_p = skip_address_list->sa_next_p[0];
2011 ;
2012 slot_p = slot_p->sa_next_p[0]) {
2013 skip_alloc_t *block_slot_p;
2014
2015 /*
2016 * switch to the free list in the middle after we've checked the
2017 * used pointer slots
2018 */
2019 if (slot_p == NULL) {
2020 checking_list_c++;
2021 if (checking_list_c == 1) {
2022 slot_p = skip_free_list->sa_next_p[0];
2023 }
2024 #if FREED_POINTER_DELAY
2025 else if (checking_list_c == 2) {
2026 slot_p = free_wait_list_head;
2027 }
2028 #endif
2029 else {
2030 /* we are done */
2031 break;
2032 }
2033 if (slot_p == NULL) {
2034 break;
2035 }
2036 }
2037
2038 /* better be in the heap */
2039 if (! IS_IN_HEAP(slot_p)) {
2040 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
2041 dmalloc_error("_dmalloc_chunk_heap_check");
2042 return 0;
2043 }
2044
2045 /*
2046 * now we look up the slot pointer itself and make sure it exists
2047 * in a valid block
2048 */
2049 block_slot_p = find_address(slot_p, 0 /* used list */,
2050 0 /* not exact pointer */, skip_update);
2051 if (block_slot_p == NULL) {
2052 dmalloc_errno = DMALLOC_ERROR_ADMIN_LIST;
2053 dmalloc_error("_dmalloc_chunk_heap_check");
2054 return 0;
2055 }
2056
2057 /* point at the block */
2058 block_p = block_slot_p->sa_mem;
2059
2060 /* check block magic */
2061 if (block_p->eb_magic1 != ENTRY_BLOCK_MAGIC1) {
2062 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
2063 dmalloc_error("_dmalloc_chunk_heap_check");
2064 return 0;
2065 }
2066
2067 /* make sure the slot level matches */
2068 if (slot_p->sa_level_n != block_p->eb_level_n) {
2069 dmalloc_errno = DMALLOC_ERROR_ADDRESS_LIST;
2070 dmalloc_error("_dmalloc_chunk_heap_check");
2071 return 0;
2072 }
2073
2074 /* now check the allocation */
2075 if (checking_list_c == 0) {
2076 ret = check_used_slot(slot_p, NULL /* no user pnt */,
2077 0 /* loose pnt checking */, 0 /* no strlen */,
2078 0 /* no min-size */);
2079 if (! ret) {
2080 /* error set in check_slot */
2081 log_error_info(NULL, 0, NULL, slot_p, "checking user pointer",
2082 "_dmalloc_chunk_heap_check");
2083 /* not a critical error */
2084 final = 0;
2085 }
2086 }
2087 else {
2088 ret = check_free_slot(slot_p);
2089 if (! ret) {
2090 /* error set in check_slot */
2091 log_error_info(NULL, 0, NULL, slot_p, "checking free pointer",
2092 "_dmalloc_chunk_heap_check");
2093 /* not a critical error */
2094 final = 0;
2095 }
2096 }
2097 }
2098
2099 return final;
2100 }
2101
2102 /*
2103 * int _dmalloc_chunk_pnt_check
2104 *
2105 * Run extensive tests on a pointer.
2106 *
2107 * Returns 1 if the pointer is okay or 0 if not
2108 *
2109 * ARGUMENTS:
2110 *
2111 * func -> Function string which is checking the pointer.
2112 *
2113 * user_pnt -> Pointer we are checking.
2114 *
2115 * exact_b -> Set to 1 to find the pointer specifically. Otherwise we
2116 * can find the pointer inside of an allocation.
2117 *
2118 * strlen_b -> Make sure that pnt can hold at least a strlen + 1
2119 * bytes. If 0 then ignore.
2120 *
2121 * min_size -> Make sure that pnt can hold at least that many bytes.
2122 * If 0 then ignore.
2123 */
_dmalloc_chunk_pnt_check(const char * func,const void * user_pnt,const int exact_b,const int strlen_b,const int min_size)2124 int _dmalloc_chunk_pnt_check(const char *func, const void *user_pnt,
2125 const int exact_b, const int strlen_b,
2126 const int min_size)
2127 {
2128 skip_alloc_t *slot_p;
2129
2130 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
2131 if (func == NULL) {
2132 dmalloc_message("checking pointer '%p'", user_pnt);
2133 }
2134 else {
2135 dmalloc_message("checking func '%s' pointer '%p'", func, user_pnt);
2136 }
2137 }
2138
2139 /* try to find the address */
2140 slot_p = find_address(user_pnt, 0 /* used list */, 0 /* not exact pointer */,
2141 skip_update);
2142 if (slot_p == NULL) {
2143 if (exact_b) {
2144 dmalloc_errno = DMALLOC_ERROR_NOT_FOUND;
2145 log_error_info(NULL, 0, user_pnt, NULL, "pointer-check", func);
2146 return 0;
2147 }
2148 else {
2149 return 1;
2150 }
2151 }
2152
2153 /* now make sure that the user slot is valid */
2154 if (! check_used_slot(slot_p, user_pnt, exact_b, strlen_b, min_size)) {
2155 /* dmalloc_error set in check_used_slot */
2156 log_error_info(NULL, 0, user_pnt, slot_p, "pointer-check", func);
2157 return 0;
2158 }
2159
2160 return 1;
2161 }
2162
2163 /************************** low-level user functions *************************/
2164
2165 /*
2166 * void *_dmalloc_chunk_malloc
2167 *
2168 * Allocate a chunk of memory.
2169 *
2170 * Returns a valid pointer on success or NULL on failure.
2171 *
2172 * ARGUMENTS:
2173 *
2174 * file -> File-name or return-address location of the allocation.
2175 *
2176 * line -> Line-number location of the allocation.
2177 *
2178 * size -> Number of bytes to allocate.
2179 *
2180 * func_id -> Calling function-id as defined in dmalloc.h.
2181 *
2182 * alignment -> If greater than 0 then try to align the returned
2183 * block.
2184 */
_dmalloc_chunk_malloc(const char * file,const unsigned int line,const unsigned long size,const int func_id,const unsigned int alignment)2185 void *_dmalloc_chunk_malloc(const char *file, const unsigned int line,
2186 const unsigned long size, const int func_id,
2187 const unsigned int alignment)
2188 {
2189 unsigned long needed_size;
2190 int valloc_b = 0, fence_b = 0;
2191 char where_buf[MAX_FILE_LENGTH + 64], disp_buf[64];
2192 skip_alloc_t *slot_p;
2193 pnt_info_t pnt_info;
2194 const char *trans_log;
2195
2196 // TOTO: is alignment used here appropriately?
2197
2198 /* counts calls to malloc */
2199 if (func_id == DMALLOC_FUNC_CALLOC) {
2200 func_calloc_c++;
2201 }
2202 else if (alignment == BLOCK_SIZE) {
2203 func_valloc_c++;
2204 valloc_b = 1;
2205 }
2206 else if (alignment > 0) {
2207 func_memalign_c++;
2208 }
2209 else if (func_id == DMALLOC_FUNC_NEW) {
2210 func_new_c++;
2211 }
2212 else if (func_id != DMALLOC_FUNC_REALLOC
2213 && func_id != DMALLOC_FUNC_RECALLOC) {
2214 func_malloc_c++;
2215 }
2216
2217 #if ALLOW_ALLOC_ZERO_SIZE == 0
2218 if (size == 0) {
2219 dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
2220 log_error_info(file, line, NULL, NULL, "bad zero byte allocation request",
2221 "malloc");
2222 return MALLOC_ERROR;
2223 }
2224 #endif
2225
2226 #if LARGEST_ALLOCATION
2227 /* have we exceeded the upper bounds */
2228 if (size > LARGEST_ALLOCATION) {
2229 dmalloc_errno = DMALLOC_ERROR_TOO_BIG;
2230 log_error_info(file, line, NULL, NULL, "allocation too big", "malloc");
2231 return MALLOC_ERROR;
2232 }
2233 #endif
2234
2235 needed_size = size;
2236
2237 /* adjust the size */
2238 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_FENCE)) {
2239 needed_size += FENCE_OVERHEAD_SIZE;
2240 fence_b = 1;
2241
2242 /*
2243 * If the user is requesting a page-aligned block of data then we
2244 * will need another block below the allocation just for the fence
2245 * information. Ugh.
2246 */
2247 if (valloc_b) {
2248 needed_size += BLOCK_SIZE;
2249 }
2250 }
2251 else if (valloc_b && needed_size <= BLOCK_SIZE / 2) {
2252 /*
2253 * If we are valloc-ing, make sure that we get a blocksized chunk
2254 * because they are always block aligned. We know here that fence
2255 * posting is not on otherwise it would have been set above.
2256 */
2257 needed_size = BLOCK_SIZE;
2258 }
2259
2260 /* get some space for our memory */
2261 slot_p = get_memory(needed_size);
2262 if (slot_p == NULL) {
2263 /* errno set in get_slot */
2264 return MALLOC_ERROR;
2265 }
2266 if (fence_b) {
2267 BIT_SET(slot_p->sa_flags, ALLOC_FLAG_FENCE);
2268 }
2269 if (valloc_b) {
2270 BIT_SET(slot_p->sa_flags, ALLOC_FLAG_VALLOC);
2271 }
2272 slot_p->sa_user_size = size;
2273
2274 /* initialize the bblocks */
2275 alloc_cur_given += slot_p->sa_total_size;
2276 alloc_max_given = MAX(alloc_max_given, alloc_cur_given);
2277
2278 get_pnt_info(slot_p, &pnt_info);
2279
2280 /* clear the allocation */
2281 clear_alloc(slot_p, &pnt_info, 0 /* no old-size */, func_id);
2282
2283 slot_p->sa_file = file;
2284 slot_p->sa_line = line;
2285 slot_p->sa_use_iter = _dmalloc_iter_c;
2286 #if LOG_PNT_SEEN_COUNT
2287 slot_p->sa_seen_c++;
2288 #endif
2289 #if LOG_PNT_ITERATION
2290 slot_p->sa_iteration = _dmalloc_iter_c;
2291 #endif
2292 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_ELAPSED_TIME)
2293 || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_CURRENT_TIME)) {
2294 #if LOG_PNT_TIMEVAL
2295 GET_TIMEVAL(slot_p->sa_timeval);
2296 #else
2297 #if LOG_PNT_TIME
2298 slot_p->sa_time = time(NULL);
2299 #endif
2300 #endif
2301 }
2302
2303 #if LOG_PNT_THREAD_ID
2304 slot_p->sa_thread_id = THREAD_GET_ID();
2305 #endif
2306
2307 /* do we need to print transaction info? */
2308 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
2309 switch (func_id) {
2310 case DMALLOC_FUNC_CALLOC:
2311 trans_log = "calloc";
2312 break;
2313 case DMALLOC_FUNC_MEMALIGN:
2314 trans_log = "memalign";
2315 break;
2316 case DMALLOC_FUNC_VALLOC:
2317 trans_log = "valloc";
2318 break;
2319 default:
2320 trans_log = "alloc";
2321 break;
2322 }
2323 dmalloc_message("*** %s: at '%s' for %ld bytes, got '%s'",
2324 trans_log,
2325 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
2326 file, line),
2327 size, display_pnt(pnt_info.pi_user_start, slot_p, disp_buf,
2328 sizeof(disp_buf)));
2329 }
2330
2331 #if MEMORY_TABLE_TOP_LOG
2332 _dmalloc_table_insert(&mem_table_alloc, file, line, size);
2333 #endif
2334
2335 /* monitor current allocation level */
2336 alloc_current += size;
2337 alloc_maximum = MAX(alloc_maximum, alloc_current);
2338 _dmalloc_alloc_total += size;
2339 alloc_one_max = MAX(alloc_one_max, size);
2340
2341 /* monitor pointer usage */
2342 alloc_cur_pnts++;
2343 alloc_max_pnts = MAX(alloc_max_pnts, alloc_cur_pnts);
2344 alloc_tot_pnts++;
2345
2346 return pnt_info.pi_user_start;
2347 }
2348
2349 /*
2350 * int _dmalloc_chunk_free
2351 *
2352 * Free a user pointer from the heap.
2353 *
2354 * Returns FREE_NOERROR on success or FREE_ERROR on failure
2355 *
2356 * ARGUMENTS:
2357 *
2358 * file -> File-name or return-address location of the allocation.
2359 *
2360 * line -> Line-number location of the allocation.
2361 *
2362 * user_pnt -> Pointer we are freeing.
2363 *
2364 * func_id -> Function ID
2365 */
_dmalloc_chunk_free(const char * file,const unsigned int line,void * user_pnt,const int func_id)2366 int _dmalloc_chunk_free(const char *file, const unsigned int line,
2367 void *user_pnt, const int func_id)
2368 {
2369 char where_buf[MAX_FILE_LENGTH + 64];
2370 char where_buf2[MAX_FILE_LENGTH + 64], disp_buf[64];
2371 skip_alloc_t *slot_p, *update_p;
2372
2373 /* counts calls to free */
2374 if (func_id == DMALLOC_FUNC_DELETE) {
2375 func_delete_c++;
2376 }
2377 else if (func_id == DMALLOC_FUNC_REALLOC
2378 || func_id == DMALLOC_FUNC_RECALLOC) {
2379 /* ignore these because they will alredy be accounted for in realloc */
2380 }
2381 else {
2382 func_free_c++;
2383 }
2384
2385 if (user_pnt == NULL) {
2386
2387 #if ALLOW_FREE_NULL_MESSAGE
2388 /* does the user want a specific message? */
2389 dmalloc_message("WARNING: tried to free(0) from '%s'",
2390 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
2391 file, line));
2392 #endif
2393
2394 /*
2395 * NOTE: we have here both a default in the settings.h file and a
2396 * runtime token in case people want to turn it on or off at
2397 * runtime.
2398 */
2399 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_ERROR_FREE_NULL)) {
2400 dmalloc_errno = DMALLOC_ERROR_IS_NULL;
2401 log_error_info(file, line, user_pnt, NULL, "invalid 0L pointer", "free");
2402 return FREE_ERROR;
2403 }
2404
2405 #if ALLOW_FREE_NULL == 0
2406 dmalloc_errno = DMALLOC_ERROR_IS_NULL;
2407 #endif
2408 return FREE_ERROR;
2409 }
2410
2411 update_p = skip_update;
2412
2413 /* try to find the address with loose match */
2414 slot_p = find_address(user_pnt, 0 /* used list */, 0 /* not exact pointer */,
2415 skip_update);
2416 if (slot_p == NULL) {
2417 #if FREED_POINTER_DELAY
2418 skip_alloc_t *del_p;
2419
2420 /* search the delay list */
2421 for (del_p = free_wait_list_head;
2422 del_p != NULL;
2423 del_p = del_p->sa_next_p[0]) {
2424 if (del_p->sa_mem <= user_pnt
2425 && (char *)del_p->sa_mem + del_p->sa_total_size > (char *)user_pnt) {
2426 pnt_info_t info;
2427 get_pnt_info(del_p, &info);
2428 if (info.pi_user_start == user_pnt) {
2429 dmalloc_errno = DMALLOC_ERROR_ALREADY_FREE;
2430 }
2431 else {
2432 dmalloc_errno = DMALLOC_ERROR_NOT_FOUND;
2433 }
2434 break;
2435 }
2436 }
2437 if (del_p == NULL) {
2438 #endif
2439 /* not in the used list so check the free list */
2440 if (find_address(user_pnt, 1 /* free list */, 0 /* not exact pointer */,
2441 skip_update) == NULL) {
2442 dmalloc_errno = DMALLOC_ERROR_NOT_FOUND;
2443 }
2444 else {
2445 dmalloc_errno = DMALLOC_ERROR_ALREADY_FREE;
2446 }
2447 #if FREED_POINTER_DELAY
2448 }
2449 #endif
2450 log_error_info(file, line, user_pnt, NULL, "finding address in heap",
2451 "free");
2452 return FREE_ERROR;
2453 }
2454
2455 if (! check_used_slot(slot_p, user_pnt, 1 /* exact pnt */, 0 /* no strlen */,
2456 0 /* no min-size */)) {
2457 /* error set in check slot */
2458 log_error_info(file, line, user_pnt, slot_p, "checking pointer admin",
2459 "free");
2460 return FREE_ERROR;
2461 }
2462
2463 if (! remove_slot(slot_p, update_p)) {
2464 /* error set and dumped in remove_slot */
2465 return FREE_ERROR;
2466 }
2467 if (BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FENCE)) {
2468 /*
2469 * We need to preserve the fence-post flag because we may need to
2470 * properly check for previously freed pointers in the future.
2471 */
2472 slot_p->sa_flags = ALLOC_FLAG_FREE | ALLOC_FLAG_FENCE;
2473 }
2474 else {
2475 slot_p->sa_flags = ALLOC_FLAG_FREE;
2476 }
2477
2478 alloc_cur_pnts--;
2479
2480 slot_p->sa_use_iter = _dmalloc_iter_c;
2481 #if LOG_PNT_SEEN_COUNT
2482 slot_p->sa_seen_c++;
2483 #endif
2484
2485 /* do we need to print transaction info? */
2486 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
2487 dmalloc_message("*** free: at '%s' pnt '%s': size %u, alloced at '%s'",
2488 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf), file,
2489 line),
2490 display_pnt(user_pnt, slot_p, disp_buf, sizeof(disp_buf)),
2491 slot_p->sa_user_size,
2492 _dmalloc_chunk_desc_pnt(where_buf2, sizeof(where_buf2),
2493 slot_p->sa_file, slot_p->sa_line));
2494 }
2495
2496 #if MEMORY_TABLE_TOP_LOG
2497 _dmalloc_table_delete(&mem_table_alloc, slot_p->sa_file, slot_p->sa_line,
2498 slot_p->sa_user_size);
2499 #endif
2500
2501 /* update the file/line -- must be after _dmalloc_table_delete */
2502 slot_p->sa_file = file;
2503 slot_p->sa_line = line;
2504
2505 /* monitor current allocation level */
2506 alloc_current -= slot_p->sa_user_size;
2507 alloc_cur_given -= slot_p->sa_total_size;
2508 free_space_bytes += slot_p->sa_total_size;
2509
2510 /* clear the memory */
2511 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_FREE_BLANK)
2512 || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_BLANK)) {
2513 memset(slot_p->sa_mem, FREE_BLANK_CHAR, slot_p->sa_total_size);
2514 /* set our slot blank flag */
2515 BIT_SET(slot_p->sa_flags, ALLOC_FLAG_BLANK);
2516 }
2517
2518 /*
2519 * The question is should we combine multiple free chunks together
2520 * into one. This would help we with fragmentation but it would
2521 * screwup the seen counter.
2522 *
2523 * Check above and below the free bblock looking for neighbors that
2524 * are free so we can add them together and put them in a different
2525 * free slot.
2526 *
2527 * NOTE: all of these block's reuse-iter count will be moved ahead
2528 * because we are encorporating in this newly freed block.
2529 */
2530
2531 if (! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_NEVER_REUSE)) {
2532 #if FREED_POINTER_DELAY
2533 slot_p->sa_next_p[0] = NULL;
2534 if (free_wait_list_head == NULL) {
2535 free_wait_list_head = slot_p;
2536 }
2537 else {
2538 free_wait_list_tail->sa_next_p[0] = slot_p;
2539 }
2540 free_wait_list_tail = slot_p;
2541 #else
2542 /* put slot on free list */
2543 if (! insert_slot(slot_p, 1 /* free list */)) {
2544 /* error dumped in insert_slot */
2545 return FREE_ERROR;
2546 }
2547 #endif
2548 }
2549
2550 return FREE_NOERROR;
2551 }
2552
2553 /*
2554 * void *_dmalloc_chunk_realloc
2555 *
2556 * Re-allocate a chunk of memory either shrinking or expanding it.
2557 *
2558 * Returns a valid pointer on success of NULL on failure.
2559 *
2560 * ARGUMENTS:
2561 *
2562 * file -> File-name or return-address location of the allocation.
2563 *
2564 * line -> Line-number location of the allocation.
2565 *
2566 * old_user_pnt -> Old user pointer that we are reallocating.
2567 *
2568 * new_size -> New-size to change the pointer.
2569 *
2570 * func_id -> Calling function-id as defined in dmalloc.h.
2571 */
_dmalloc_chunk_realloc(const char * file,const unsigned int line,void * old_user_pnt,const unsigned long new_size,const int func_id)2572 void *_dmalloc_chunk_realloc(const char *file, const unsigned int line,
2573 void *old_user_pnt,
2574 const unsigned long new_size,
2575 const int func_id)
2576 {
2577 const char *old_file;
2578 skip_alloc_t *slot_p;
2579 pnt_info_t pnt_info;
2580 void *new_user_pnt;
2581 unsigned int old_size, old_line;
2582
2583 /* counts calls to realloc */
2584 if (func_id == DMALLOC_FUNC_RECALLOC) {
2585 func_recalloc_c++;
2586 }
2587 else {
2588 func_realloc_c++;
2589 }
2590
2591 #if ALLOW_ALLOC_ZERO_SIZE == 0
2592 if (new_size == 0) {
2593 dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
2594 log_error_info(file, line, NULL, NULL, "bad zero byte allocation request",
2595 "realloc");
2596 return REALLOC_ERROR;
2597 }
2598 #endif
2599
2600 /* by now malloc.c should have taken care of the realloc(NULL) case */
2601 if (old_user_pnt == NULL) {
2602 dmalloc_errno = DMALLOC_ERROR_IS_NULL;
2603 log_error_info(file, line, old_user_pnt, NULL, "invalid pointer",
2604 "realloc");
2605 return REALLOC_ERROR;
2606 }
2607
2608 /* find the old pointer with loose checking for fence post stuff */
2609 slot_p = find_address(old_user_pnt, 0 /* used list */,
2610 0 /* not exact pointer */, skip_update);
2611 if (slot_p == NULL) {
2612 dmalloc_errno = DMALLOC_ERROR_NOT_FOUND;
2613 log_error_info(file, line, old_user_pnt, NULL, "finding address in heap",
2614 "realloc");
2615 return 0;
2616 }
2617
2618 /* get info about the pointer */
2619 get_pnt_info(slot_p, &pnt_info);
2620 old_file = slot_p->sa_file;
2621 old_line = slot_p->sa_line;
2622 old_size = slot_p->sa_user_size;
2623
2624 /* if we are not realloc copying and the size is the same */
2625 if ((char *)pnt_info.pi_user_start + new_size >
2626 (char *)pnt_info.pi_upper_bounds
2627 || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_REALLOC_COPY)
2628 || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_NEVER_REUSE)) {
2629 int min_size;
2630
2631 /* allocate space for new chunk */
2632 new_user_pnt = _dmalloc_chunk_malloc(file, line, new_size, func_id,
2633 0 /* no align */);
2634 if (new_user_pnt == MALLOC_ERROR) {
2635 return REALLOC_ERROR;
2636 }
2637
2638 /*
2639 * NOTE: _chunk_malloc() already took care of the fence stuff and
2640 * zeroing of memory.
2641 */
2642
2643 /* copy stuff into new section of memory */
2644 min_size = MIN(new_size, old_size);
2645 if (min_size > 0) {
2646 memcpy(new_user_pnt, pnt_info.pi_user_start, min_size);
2647 }
2648
2649 /* free old pointer */
2650 if (_dmalloc_chunk_free(file, line, old_user_pnt,
2651 func_id) != FREE_NOERROR) {
2652 return REALLOC_ERROR;
2653 }
2654 }
2655 else {
2656 /* new pointer is the same as the old one */
2657 new_user_pnt = pnt_info.pi_user_start;
2658
2659 /*
2660 * monitor current allocation level
2661 *
2662 * NOTE: we do this here since the malloc/free used above take care
2663 * on if in that section
2664 */
2665 alloc_current += new_size - old_size;
2666 alloc_maximum = MAX(alloc_maximum, alloc_current);
2667 _dmalloc_alloc_total += new_size;
2668 alloc_one_max = MAX(alloc_one_max, new_size);
2669
2670 /* monitor pointer usage */
2671 alloc_tot_pnts++;
2672
2673 /* change the slot information */
2674 slot_p->sa_user_size = new_size;
2675 get_pnt_info(slot_p, &pnt_info);
2676
2677 clear_alloc(slot_p, &pnt_info, old_size, func_id);
2678
2679 slot_p->sa_use_iter = _dmalloc_iter_c;
2680 #if LOG_PNT_SEEN_COUNT
2681 /* we see in inbound and outbound so we need to increment by 2 */
2682 slot_p->sa_seen_c += 2;
2683 #endif
2684
2685 #if MEMORY_TABLE_TOP_LOG
2686 _dmalloc_table_delete(&mem_table_alloc, slot_p->sa_file, slot_p->sa_line,
2687 old_size);
2688 _dmalloc_table_insert(&mem_table_alloc, file, line, new_size);
2689 #endif
2690
2691 /*
2692 * finally, we update the file/line info -- must be after
2693 * _dmalloc_table functions
2694 */
2695 slot_p->sa_file = file;
2696 slot_p->sa_line = line;
2697 }
2698
2699 if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
2700 const char *trans_log;
2701 char where_buf[MAX_FILE_LENGTH + 64];
2702 char where_buf2[MAX_FILE_LENGTH + 64];
2703
2704 if (func_id == DMALLOC_FUNC_RECALLOC) {
2705 trans_log = "recalloc";
2706 }
2707 else {
2708 trans_log = "realloc";
2709 }
2710 dmalloc_message("*** %s: at '%s' from '%p' (%u bytes) file '%s' to '%p' (%lu bytes)",
2711 trans_log,
2712 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
2713 file, line),
2714 old_user_pnt, old_size,
2715 _dmalloc_chunk_desc_pnt(where_buf2, sizeof(where_buf2),
2716 old_file, old_line),
2717 new_user_pnt, new_size);
2718 }
2719
2720 return new_user_pnt;
2721 }
2722
2723 /***************************** diagnostic routines ***************************/
2724
2725 /*
2726 * void _dmalloc_chunk_log_stats
2727 *
2728 * Log general statistics from the heap to the logfile.
2729 */
_dmalloc_chunk_log_stats(void)2730 void _dmalloc_chunk_log_stats(void)
2731 {
2732 unsigned long overhead, user_space, tot_space;
2733
2734 dmalloc_message("Dumping Chunk Statistics:");
2735
2736 tot_space = (user_block_c + admin_block_c) * BLOCK_SIZE;
2737 user_space = alloc_current + free_space_bytes;
2738 overhead = admin_block_c * BLOCK_SIZE;
2739
2740 /* version information */
2741 dmalloc_message("basic-block %d bytes, alignment %d bytes",
2742 BLOCK_SIZE, ALLOCATION_ALIGNMENT);
2743
2744 /* general heap information with blocks */
2745 unsigned long diff = (PNT_ARITH_TYPE)_dmalloc_heap_high -
2746 (PNT_ARITH_TYPE)_dmalloc_heap_low;
2747 dmalloc_message("heap address range: %p to %p, %lu bytes",
2748 _dmalloc_heap_low, _dmalloc_heap_high, diff);
2749 dmalloc_message(" user blocks: %ld blocks, %ld bytes (%ld%%)",
2750 user_block_c, user_space,
2751 (tot_space < 100 ? 0 : user_space / (tot_space / 100)));
2752 dmalloc_message(" admin blocks: %ld blocks, %ld bytes (%ld%%)",
2753 admin_block_c, overhead,
2754 (tot_space < 100 ? 0 : overhead / (tot_space / 100)));
2755 dmalloc_message(" total blocks: %ld blocks, %ld bytes",
2756 user_block_c + admin_block_c, tot_space);
2757
2758 dmalloc_message("heap checked %ld", heap_check_c);
2759
2760 /* log user allocation information */
2761 dmalloc_message("alloc calls: malloc %lu, calloc %lu, realloc %lu, free %lu",
2762 func_malloc_c, func_calloc_c, func_realloc_c, func_free_c);
2763 dmalloc_message("alloc calls: recalloc %lu, memalign %lu, valloc %lu",
2764 func_recalloc_c, func_memalign_c, func_valloc_c);
2765 dmalloc_message("alloc calls: new %lu, delete %lu",
2766 func_new_c, func_delete_c);
2767 dmalloc_message(" current memory in use: %lu bytes (%lu pnts)",
2768 alloc_current, alloc_cur_pnts);
2769 dmalloc_message(" total memory allocated: %lu bytes (%lu pnts)",
2770 _dmalloc_alloc_total, alloc_tot_pnts);
2771
2772 /* maximum stats */
2773 dmalloc_message(" max in use at one time: %lu bytes (%lu pnts)",
2774 alloc_maximum, alloc_max_pnts);
2775 dmalloc_message("max alloced with 1 call: %lu bytes",
2776 alloc_one_max);
2777 dmalloc_message("max unused memory space: %lu bytes (%lu%%)",
2778 alloc_max_given - alloc_maximum,
2779 (alloc_max_given == 0 ? 0 :
2780 ((alloc_max_given - alloc_maximum) * 100) /
2781 alloc_max_given));
2782
2783 #if MEMORY_TABLE_TOP_LOG
2784 dmalloc_message("top %d allocations:", MEMORY_TABLE_TOP_LOG);
2785 _dmalloc_table_log_info(&mem_table_alloc, MEMORY_TABLE_TOP_LOG,
2786 1 /* have in-use column */);
2787 #endif
2788 }
2789
2790 /*
2791 * void _dmalloc_chunk_log_changed
2792 *
2793 * Log the pointers that has changed since a pointer in time.
2794 *
2795 * ARGUMENTS:
2796 *
2797 * mark -> Dmalloc counter used to mark a specific time so that
2798 * servers can check on the changed pointers.
2799 *
2800 * log_non_free_b -> If set to 1 then log the new not-freed
2801 * (i.e. used) pointers.
2802 *
2803 * log_free_b -> If set to 1 then log the new freed pointers.
2804 *
2805 * details_b -> If set to 1 then dump the individual pointer entries
2806 * instead of just the summary.
2807 */
_dmalloc_chunk_log_changed(const unsigned long mark,const int log_not_freed_b,const int log_freed_b,const int details_b)2808 void _dmalloc_chunk_log_changed(const unsigned long mark,
2809 const int log_not_freed_b,
2810 const int log_freed_b, const int details_b)
2811 {
2812 skip_alloc_t *slot_p;
2813 pnt_info_t pnt_info;
2814 int known_b, freed_b, used_b;
2815 char out[DUMP_SPACE * 4], *which_str;
2816 char where_buf[MAX_FILE_LENGTH + 64], disp_buf[64];
2817 int unknown_size_c = 0, unknown_block_c = 0, out_len;
2818 int size_c = 0, block_c = 0, checking_list_c = 0;
2819
2820 if (log_not_freed_b && log_freed_b) {
2821 which_str = "Not-Freed and Freed";
2822 }
2823 else if (log_not_freed_b) {
2824 which_str = "Not-Freed";
2825 }
2826 else if (log_freed_b) {
2827 which_str = "Freed";
2828 }
2829 else {
2830 return;
2831 }
2832
2833 if (mark == 0) {
2834 dmalloc_message("Dumping %s Pointers Changed Since Start:", which_str);
2835 }
2836 else {
2837 dmalloc_message("Dumping %s Pointers Changed Since Mark %lu:",
2838 which_str, mark);
2839 }
2840
2841 /* clear out our memory table so we can fill it with pointer info */
2842 _dmalloc_table_init(&mem_table_changed, mem_table_changed_entries,
2843 sizeof(mem_table_changed_entries) /
2844 sizeof(*mem_table_changed_entries));
2845
2846 /* run through the blocks */
2847 for (slot_p = skip_address_list->sa_next_p[0];
2848 ;
2849 slot_p = slot_p->sa_next_p[0]) {
2850
2851 /*
2852 * switch to the free list in the middle after we've checked the
2853 * used pointer slots
2854 */
2855 if (slot_p == NULL) {
2856 checking_list_c++;
2857 if (checking_list_c == 1) {
2858 slot_p = skip_free_list->sa_next_p[0];
2859 }
2860 #if FREED_POINTER_DELAY
2861 else if (checking_list_c == 2) {
2862 slot_p = free_wait_list_head;
2863 }
2864 #endif
2865 else {
2866 /* we are done */
2867 break;
2868 }
2869 if (slot_p == NULL) {
2870 break;
2871 }
2872 }
2873
2874 freed_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FREE);
2875 used_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_USER);
2876
2877 /*
2878 * check for different types
2879 */
2880 if (! (freed_b || used_b)) {
2881 continue;
2882 }
2883
2884 /* do we want to dump this one? */
2885 if (! ((log_not_freed_b && used_b) || (log_freed_b && freed_b))) {
2886 continue;
2887 }
2888 /* is it too long ago? */
2889 if (slot_p->sa_use_iter <= mark) {
2890 continue;
2891 }
2892
2893 /* unknown pointer? */
2894 if (slot_p->sa_file == DMALLOC_DEFAULT_FILE
2895 || slot_p->sa_line == DMALLOC_DEFAULT_LINE) {
2896 unknown_block_c++;
2897 unknown_size_c += slot_p->sa_user_size;
2898 known_b = 0;
2899 }
2900 else {
2901 known_b = 1;
2902 }
2903
2904 get_pnt_info(slot_p, &pnt_info);
2905
2906 if (known_b || (! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_KNOWN))) {
2907 if (details_b) {
2908 dmalloc_message(" %s freed: '%s' (%u bytes) from '%s'",
2909 (freed_b ? " " : "not"),
2910 display_pnt(pnt_info.pi_user_start, slot_p, disp_buf,
2911 sizeof(disp_buf)),
2912 slot_p->sa_user_size,
2913 _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf),
2914 slot_p->sa_file,
2915 slot_p->sa_line));
2916
2917 if ((! freed_b)
2918 && BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_NONFREE_SPACE)) {
2919 out_len = expand_chars((char *)pnt_info.pi_user_start, DUMP_SPACE,
2920 out, sizeof(out));
2921 dmalloc_message(" dump of '%p': '%.*s'",
2922 pnt_info.pi_user_start, out_len, out);
2923 }
2924 }
2925 _dmalloc_table_insert(&mem_table_changed, slot_p->sa_file,
2926 slot_p->sa_line, slot_p->sa_user_size);
2927 }
2928 }
2929
2930 /* dump the summary from the table table */
2931 _dmalloc_table_log_info(&mem_table_changed, 0 /* log all entries */,
2932 0 /* no in-use column */);
2933
2934 /* copy out size of pointers */
2935 if (block_c > 0) {
2936 if (block_c - unknown_block_c > 0) {
2937 dmalloc_message(" known memory: %d pointer%s, %d bytes",
2938 block_c - unknown_block_c,
2939 (block_c - unknown_block_c == 1 ? "" : "s"),
2940 size_c - unknown_size_c);
2941 }
2942 if (unknown_block_c > 0) {
2943 dmalloc_message(" unknown memory: %d pointer%s, %d bytes",
2944 unknown_block_c, (unknown_block_c == 1 ? "" : "s"),
2945 unknown_size_c);
2946 }
2947 }
2948 }
2949
2950 /*
2951 * unsigned long _dmalloc_chunk_count_changed
2952 *
2953 * Return the pointers that has changed since a pointer in time.
2954 *
2955 * Returns the number of bytes changed since mark.
2956 *
2957 * ARGUMENTS:
2958 *
2959 * mark -> Dmalloc counter used to mark a specific time so that
2960 * servers can check on the changed pointers.
2961 *
2962 * count_non_free_b -> If set to 1 then count the new not-freed
2963 * (i.e. used) pointers.
2964 *
2965 * count_free_b -> If set to 1 then count the new freed pointers.
2966 */
_dmalloc_chunk_count_changed(const unsigned long mark,const int count_not_freed_b,const int count_freed_b)2967 unsigned long _dmalloc_chunk_count_changed(const unsigned long mark,
2968 const int count_not_freed_b,
2969 const int count_freed_b)
2970 {
2971 skip_alloc_t *slot_p;
2972 int freed_b, used_b;
2973 int checking_list_c = 0;
2974 unsigned int mem_count = 0;
2975
2976 /* run through the blocks */
2977 for (slot_p = skip_address_list->sa_next_p[0];
2978 ;
2979 slot_p = slot_p->sa_next_p[0]) {
2980
2981 /*
2982 * switch to the free list in the middle after we've checked the
2983 * used pointer slots
2984 */
2985 if (slot_p == NULL) {
2986 checking_list_c++;
2987 if (checking_list_c == 1) {
2988 slot_p = skip_free_list->sa_next_p[0];
2989 }
2990 #if FREED_POINTER_DELAY
2991 else if (checking_list_c == 2) {
2992 slot_p = free_wait_list_head;
2993 }
2994 #endif
2995 else {
2996 /* we are done */
2997 break;
2998 }
2999 if (slot_p == NULL) {
3000 break;
3001 }
3002 }
3003
3004 freed_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_FREE);
3005 used_b = BIT_IS_SET(slot_p->sa_flags, ALLOC_FLAG_USER);
3006
3007 /*
3008 * check for different types
3009 */
3010 if (! (freed_b || used_b)) {
3011 continue;
3012 }
3013 /* is it too long ago? */
3014 if (slot_p->sa_use_iter <= mark) {
3015 continue;
3016 }
3017
3018 /* count the memory */
3019 if (count_not_freed_b && used_b) {
3020 mem_count += slot_p->sa_user_size;
3021 }
3022 else if (count_freed_b && freed_b) {
3023 mem_count += slot_p->sa_user_size;
3024 }
3025 }
3026
3027 return mem_count;
3028 }
3029
3030 /*
3031 * void _dmalloc_chunk_get_stats
3032 *
3033 * Return a number of statistics about the current heap.
3034 *
3035 * ARGUMENTS:
3036 *
3037 * heap_low_p <- Pointer to pointer which, if not 0L, will be set to
3038 * the low address in the heap.
3039 *
3040 * heap_high_p <- Pointer to pointer which, if not 0L, will be set to
3041 * the high address in the heap.
3042 *
3043 * total_space_p <- Pointer to an unsigned long which, if not 0L, will
3044 * be set to the total space managed by the library including user
3045 * space, administrative space, and overhead.
3046 *
3047 * user_space_p <- Pointer to an unsigned long which, if not 0L, will
3048 * be set to the space given to the user process (allocated and free).
3049 *
3050 * current_allocated_p <- Pointer to an unsigned long which, if not
3051 * 0L, will be set to the current allocated space given to the user
3052 * process.
3053 *
3054 * current_pnt_np <- Pointer to an unsigned long which, if not 0L,
3055 * will be set to the current number of pointers allocated by the user
3056 * process.
3057 *
3058 * max_allocated_p <- Pointer to an unsigned long which, if not 0L,
3059 * will be set to the maximum allocated space given to the user
3060 * process.
3061 *
3062 * max_pnt_np <- Pointer to an unsigned long which, if not 0L, will be
3063 * set to the maximum number of pointers allocated by the user
3064 * process.
3065 *
3066 * max_one_p <- Pointer to an unsigned long which, if not 0L, will be
3067 * set to the maximum allocated with 1 call by the user process.
3068 */
_dmalloc_chunk_get_stats(void ** heap_low_p,void ** heap_high_p,unsigned long * total_space_p,unsigned long * user_space_p,unsigned long * current_allocated_p,unsigned long * current_pnt_np,unsigned long * max_allocated_p,unsigned long * max_pnt_np,unsigned long * max_one_p)3069 void _dmalloc_chunk_get_stats(void **heap_low_p, void **heap_high_p,
3070 unsigned long *total_space_p,
3071 unsigned long *user_space_p,
3072 unsigned long *current_allocated_p,
3073 unsigned long *current_pnt_np,
3074 unsigned long *max_allocated_p,
3075 unsigned long *max_pnt_np,
3076 unsigned long *max_one_p)
3077 {
3078 SET_POINTER(heap_low_p, _dmalloc_heap_low);
3079 SET_POINTER(heap_high_p, _dmalloc_heap_high);
3080 SET_POINTER(total_space_p, (user_block_c + admin_block_c) * BLOCK_SIZE);
3081 SET_POINTER(user_space_p, alloc_current + free_space_bytes);
3082 SET_POINTER(current_allocated_p, alloc_current);
3083 SET_POINTER(current_pnt_np, alloc_cur_pnts);
3084 SET_POINTER(max_allocated_p, alloc_maximum);
3085 SET_POINTER(max_pnt_np, alloc_max_pnts);
3086 SET_POINTER(max_one_p, alloc_one_max);
3087 }
3088