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