1 /*
2  * user-level memory-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 the user-level calls to the memory allocation
24  * routines.  It handles a lot of the miscellaneous support garbage for
25  * chunk.c which is the real heap manager.
26  */
27 
28 #if HAVE_STDIO_H
29 # include <stdio.h>				/* for FILE */
30 #endif
31 #if HAVE_STDLIB_H
32 # include <stdlib.h>				/* for atexit */
33 #endif
34 #if HAVE_STRING_H
35 # include <string.h>				/* for strlen */
36 #endif
37 #if HAVE_UNISTD_H
38 # include <unistd.h>				/* for write */
39 #endif
40 
41 /*
42  * cygwin includes
43  */
44 #if HAVE_SYS_CYGWIN_H
45 # include <sys/cygwin.h>
46 #endif
47 #if HAVE_STDARG_H
48 # include <stdarg.h>
49 #endif
50 #if HAVE_W32API_WINDEF_H
51 # include <w32api/windef.h>
52 #endif
53 #if HAVE_W32API_WINBASE_H
54 # include <w32api/winbase.h>
55 #endif
56 
57 #include "conf.h"				/* up here for _INCLUDE */
58 
59 #if LOG_PNT_TIMEVAL
60 # ifdef TIMEVAL_INCLUDE
61 #  include TIMEVAL_INCLUDE
62 # endif
63 #else
64 # if HAVE_TIME /* NOT LOG_PNT_TIME */
65 #  ifdef TIME_INCLUDE
66 #   include TIME_INCLUDE
67 #  endif
68 # endif
69 #endif
70 
71 #if LOCK_THREADS
72 #if HAVE_PTHREAD_H
73 #include <pthread.h>
74 #endif
75 #if HAVE_PTHREADS_H
76 #include <pthreads.h>
77 #endif
78 #endif
79 
80 #if SIGNAL_OKAY && HAVE_SIGNAL_H
81 #include <signal.h>
82 #endif
83 
84 #define DMALLOC_DISABLE
85 
86 #include "dmalloc.h"
87 
88 #include "append.h"
89 #include "chunk.h"
90 #include "compat.h"
91 #include "debug_tok.h"
92 #include "env.h"
93 #include "error.h"
94 #include "error_val.h"
95 #include "heap.h"
96 #include "dmalloc_loc.h"
97 #include "user_malloc.h"
98 #include "return.h"
99 
100 #if LOCK_THREADS
101 #if IDENT_WORKS
102 #ident "@(#) $Information: lock-threads is enabled $"
103 #else
104 static char *information = "@(#) $Information: lock-threads is enabled $";
105 #endif
106 #endif
107 
108 #define INT_TYPE	int
109 
110 /* exported variables */
111 
112 /* internal dmalloc error number for reference purposes only */
113 int		dmalloc_errno = DMALLOC_ERROR_NONE;
114 
115 /* logfile for dumping dmalloc info, DMALLOC_LOGFILE env var overrides this */
116 char		*dmalloc_logpath = NULL;
117 
118 /* local variables */
119 static	int		enabled_b = 0;		/* have we started yet? */
120 static	int		in_alloc_b = 0;		/* can't be here twice */
121 static	int		do_shutdown_b = 0;	/* execute shutdown soon */
122 static	int		memalign_warn_b = 0;	/* memalign warning printed?*/
123 static	dmalloc_track_t	tracking_func = NULL;	/* memory trxn tracking func */
124 
125 /* debug variables */
126 static	char		*start_file = NULL;	/* file to start at */
127 static	int		start_line = 0;		/* line to start */
128 static	unsigned long	start_iter = 0;		/* start after X iterations */
129 static	unsigned long	start_size = 0;		/* start after X bytes */
130 static	int		thread_lock_c = 0;	/* lock counter */
131 
132 /****************************** thread locking *******************************/
133 
134 #if LOCK_THREADS
135 #ifdef THREAD_MUTEX_T
136 /*
137  * Define a global variable we use as a lock counter.
138  *
139  * NOTE: we do not use the PTHREAD_MUTEX_INITIALIZER since this
140  * basically delays the pthread_mutex_init call to when
141  * pthread_mutex_lock is called for the first time (at least on
142  * freebsd).  Since we don't want to go recursive into the pthread
143  * library when we go to lock our mutex variable, we want to force the
144  * initialization to happen beforehand with a call to
145  * pthread_mute_init.
146  */
147 static THREAD_MUTEX_T dmalloc_mutex;
148 #else
149 #error We need to have THREAD_MUTEX_T defined by the configure script
150 #endif
151 #endif
152 
153 /*
154  * THREADS LOCKING:
155  *
156  * Because we need to protect for multiple threads making calls into
157  * the dmalloc library at the same time, we need to initialize and use
158  * a thread mutex variable.  The problem is that most thread libraries
159  * uses malloc itself and do not like to go recursive.
160  *
161  * There are two places where we may have this problem.  One is when
162  * we try to use our mutex-lock variable when pthreads is starting up
163  * in a shaky state.  The thread library allocates some space, the
164  * dmalloc library needs to lock its mutex variable so calls back into
165  * the pthread library before it is ready for a call, and a core dump
166  * is probably the result.
167  *
168  * We hopefully solve this by having the dmalloc library not lock
169  * during the first couple of memory transactions.  The number is
170  * controlled by lock-on dmalloc program environmental setting (set
171  * with ``dmalloc -o X'').  You will have to play with the value.  Too
172  * many will cause two threads to march into the dmalloc code at the
173  * same time generating a DMALLOC_ERROR_IN_TWICE error.  Too few and
174  * you will get a core dump in the pthreads initialization code.
175  *
176  * The second place where we might go recursive is when we go to
177  * actually initialize our mutex-lock before we can use it.  The
178  * THREAD_INIT_LOCK variable (in settings.h) defines that the
179  * initialization happens 2 memory transactions before the library
180  * begins to use the mutex (lock-on - 2).  It we waited to initialize
181  * the variable right before we used it, the pthread library might
182  * want to allocate some memory for the variable causing a recursive
183  * call and probably a seg-fault -- at least in OSF.  If people need
184  * to have this variable also be runtime configurable or would like to
185  * present an alternative default, please let me know.
186  */
187 
188 #if LOCK_THREADS
189 /*
190  * mutex lock the malloc library
191  */
lock_thread(void)192 static	void	lock_thread(void)
193 {
194   /* we only lock if the lock-on counter has reached 0 */
195   if (thread_lock_c == 0) {
196 #if HAVE_PTHREAD_MUTEX_LOCK
197     pthread_mutex_lock(&dmalloc_mutex);
198 #endif
199   }
200 }
201 
202 /*
203  * mutex unlock the malloc library
204  */
unlock_thread(void)205 static	void	unlock_thread(void)
206 {
207   /* if the lock-on counter has not reached 0 then count it down */
208   if (thread_lock_c > 0) {
209     thread_lock_c--;
210     /*
211      * As we approach the time when we start mutex locking the
212      * library, we need to init the mutex variable.  This sets how
213      * many times before we start locking should we init the variable
214      * taking in account that the init itself might generate a call
215      * into the library.  Ugh.
216      */
217     if (thread_lock_c == THREAD_INIT_LOCK) {
218 #if HAVE_PTHREAD_MUTEX_INIT
219       /*
220        * NOTE: we do not use the PTHREAD_MUTEX_INITIALIZER since this
221        * basically delays the pthread_mutex_init call to when
222        * pthread_mutex_lock is called for the first time (at least on
223        * freebsd).  Since we don't want to go recursive into the
224        * pthread library when we go to lock our mutex variable, we
225        * want to force the initialization to happen beforehand with a
226        * call to pthread_mute_init.
227        */
228       pthread_mutex_init(&dmalloc_mutex, THREAD_LOCK_INIT_VAL);
229 #endif
230     }
231   }
232   else if (thread_lock_c == 0) {
233 #if HAVE_PTHREAD_MUTEX_UNLOCK
234     pthread_mutex_unlock(&dmalloc_mutex);
235 #endif
236   }
237 }
238 #endif
239 
240 /****************************** local utilities ******************************/
241 
242 /*
243  * check out a pointer to see if we were looking for it.  this should
244  * be re-entrant and it may not return.
245  */
check_pnt(const char * file,const int line,const void * pnt,const char * label)246 static	void	check_pnt(const char *file, const int line, const void *pnt,
247 			  const char *label)
248 {
249   static unsigned long	addr_c = 0;
250   char			where_buf[64];
251 
252   if (_dmalloc_address == NULL || pnt != _dmalloc_address) {
253     return;
254   }
255 
256   addr_c++;
257   dmalloc_message("address '%p' found in '%s' at pass %ld from '%s'",
258 		  pnt, label, addr_c,
259 		  _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf), file,
260 					  line));
261 
262   /* NOTE: if address_seen_n == 0 then never quit */
263   if (_dmalloc_address_seen_n > 0 && addr_c >= _dmalloc_address_seen_n) {
264     dmalloc_errno = DMALLOC_ERROR_IS_FOUND;
265     dmalloc_error("check_pnt");
266   }
267 }
268 
process_environ(const char * option_str)269 static	void	process_environ(const char *option_str)
270 {
271   /*
272    * we have a static here so we can store the string without getting
273    * into problems
274    */
275   static char	options[1024];
276 
277   /* process the options flag */
278   if (option_str == NULL) {
279     options[0] = '\0';
280   }
281   else {
282     strncpy(options, option_str, sizeof(options));
283     options[sizeof(options) - 1] = '\0';
284   }
285 
286   char *previous_logpath = dmalloc_logpath;
287   _dmalloc_environ_process(options, &_dmalloc_address,
288 			   (unsigned long *)&_dmalloc_address_seen_n, &_dmalloc_flags,
289 			   &_dmalloc_check_interval, &_dmalloc_lock_on,
290 			   &dmalloc_logpath, &start_file, &start_line,
291 			   &start_iter, &start_size, &_dmalloc_memory_limit);
292   thread_lock_c = _dmalloc_lock_on;
293 
294   /* if we set the start stuff, then check-heap comes on later */
295   if (start_iter > 0 || start_size > 0) {
296     BIT_CLEAR(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
297   }
298 
299   /* indicate that we should reopen the logfile if we need to */
300   if (previous_logpath == 0L || dmalloc_logpath == 0L
301       || strcmp(previous_logpath, dmalloc_logpath) != 0) {
302     _dmalloc_reopen_log();
303   }
304 
305 #if LOCK_THREADS == 0
306   /* was thread-lock-on specified but not configured? */
307   if (_dmalloc_lock_on > 0) {
308     dmalloc_errno = DMALLOC_ERROR_LOCK_NOT_CONFIG;
309     _dmalloc_die(0);
310   }
311 #endif
312 }
313 
314 /************************** startup/shutdown calls ***************************/
315 
316 #if SIGNAL_OKAY
317 /*
318  * signal catcher
319  */
signal_handler(const int sig)320 static	RETSIGTYPE	signal_handler(const int sig)
321 {
322   dmalloc_message("caught signal %d", sig);
323   /* if we are already inside malloc then do the shutdown later */
324   if (in_alloc_b) {
325     do_shutdown_b = 1;
326   }
327   else {
328     dmalloc_shutdown();
329   }
330 }
331 #endif
332 
333 /*
334  * startup the memory-allocation module
335  */
dmalloc_startup(const char * debug_str)336 static	int	dmalloc_startup(const char *debug_str)
337 {
338   static int	some_up_b = 0;
339   const char	*env_str;
340   char		env_buf[256];
341 
342   /* have we started already? */
343   if (enabled_b) {
344     return 0;
345   }
346 
347   if (! some_up_b) {
348     /* set this up here so if an error occurs below, it will not try again */
349     some_up_b = 1;
350 
351 #if LOG_PNT_TIMEVAL
352     GET_TIMEVAL(_dmalloc_start);
353 #else
354 #if HAVE_TIME /* NOT LOG_PNT_TIME */
355     _dmalloc_start = time(NULL);
356 #endif
357 #endif
358 
359     if (debug_str == NULL) {
360       env_str = loc_getenv(OPTIONS_ENVIRON, env_buf, sizeof(env_buf),
361 			   1 /* stay safe */);
362     }
363     else {
364       env_str = debug_str;
365     }
366     /* process the environmental variable(s) */
367     process_environ(env_str);
368 
369     /*
370      * Tune the environment here.  If we have a start-file,
371      * start-count, or interval enabled then make sure the check-heap
372      * flag is cleared.
373      */
374     if (start_file != NULL
375 	|| start_iter > 0
376 	|| start_size > 0
377 	|| _dmalloc_check_interval > 0) {
378       BIT_CLEAR(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
379     }
380 
381     /* startup heap code */
382     if (! _dmalloc_heap_startup()) {
383       return 0;
384     }
385 
386     /* startup the chunk lower-level code */
387     if (! _dmalloc_chunk_startup()) {
388       return 0;
389     }
390   }
391 
392 #if LOCK_THREADS
393   if (thread_lock_c > 0) {
394     return 1;
395   }
396 #endif
397 
398   /*
399    * We have initialized all of our code.
400    *
401    * NOTE: set this up here so if an error occurs below, it will not
402    * try again
403    */
404   enabled_b = 1;
405 
406   /*
407    * NOTE: we may go recursive below here becasue atexit or on_exit
408    * may ask for memory to be allocated.  We won't worry about it and
409    * will just give it to them.  We hope that atexit didn't start the
410    * allocating.  Ugh.
411    */
412 #if AUTO_SHUTDOWN
413   /* NOTE: I use the else here in case some dumb systems has both */
414 #if HAVE_ATEXIT
415   (void)atexit(dmalloc_shutdown);
416 #else
417 #if HAVE_ON_EXIT
418   (void)on_exit(dmalloc_shutdown, NULL);
419 #endif /* HAVE_ON_EXIT */
420 #endif /* ! HAVE_ATEXIT */
421 #endif /* AUTO_SHUTDOWN */
422 
423 #if SIGNAL_OKAY
424   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CATCH_SIGNALS)) {
425 #ifdef SIGNAL1
426     (void)signal(SIGNAL1, signal_handler);
427 #endif
428 #ifdef SIGNAL2
429     (void)signal(SIGNAL2, signal_handler);
430 #endif
431 #ifdef SIGNAL3
432     (void)signal(SIGNAL3, signal_handler);
433 #endif
434 #ifdef SIGNAL4
435     (void)signal(SIGNAL4, signal_handler);
436 #endif
437 #ifdef SIGNAL5
438     (void)signal(SIGNAL5, signal_handler);
439 #endif
440 #ifdef SIGNAL6
441     (void)signal(SIGNAL6, signal_handler);
442 #endif
443   }
444 #endif /* SIGNAL_OKAY */
445 
446   return 1;
447 }
448 
449 /*
450  * static int dmalloc_in
451  *
452  * Call to the alloc routines has been made.  Do some initialization,
453  * locking, and check some debug variables.
454  *
455  * Returns 1 on success or 0 on failure.
456  *
457  * ARGUMENTS:
458  *
459  * file -> File-name or return-address of the caller.
460  *
461  * line -> Line-number of the caller.
462  *
463  * check_heap_b -> Set to 1 if it is okay to check the heap.  If set
464  * to 0 then the caller will check it itself or it is a non-invasive
465  * call.
466  */
dmalloc_in(const char * file,const int line,const int check_heap_b)467 static	int	dmalloc_in(const char *file, const int line,
468 			   const int check_heap_b)
469 {
470   if (_dmalloc_aborting_b) {
471     return 0;
472   }
473 
474   /*
475    * NOTE: we need to do this outside of lock to get env vars
476    * otherwise our _dmalloc_lock_on variable won't be initialized and
477    * the THREAD_LOCK will flip.
478    */
479   if (! enabled_b) {
480     if (! dmalloc_startup(NULL /* no options string */)) {
481       return 0;
482     }
483   }
484 
485 #if LOCK_THREADS
486   lock_thread();
487 #endif
488 
489   if (in_alloc_b) {
490     dmalloc_errno = DMALLOC_ERROR_IN_TWICE;
491     dmalloc_error("dmalloc_in");
492     /* NOTE: dmalloc_error may die already */
493     _dmalloc_die(0);
494     /*NOTREACHED*/
495   }
496 
497   in_alloc_b = 1;
498 
499   /* increment our interval */
500   _dmalloc_iter_c++;
501 
502   /* check start file/line specifications */
503   if ((! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP))
504       && start_file != NULL
505       && file != DMALLOC_DEFAULT_FILE
506       && line != DMALLOC_DEFAULT_LINE
507       && strcmp(start_file, file) == 0
508       && (start_line == 0 || start_line == line)) {
509     BIT_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
510     /*
511      * we disable the start file so we won't check this again and the
512      * interval can go on/off
513      */
514     start_file = NULL;
515   }
516 
517   /* start checking heap after X times */
518   else if (start_iter > 0) {
519     if (--start_iter == 0) {
520       BIT_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
521       /*
522        * this is automatically disabled since it goes to 0 so the
523        * interval can go on/off
524        */
525     }
526   }
527 
528   else if (start_size > 0 && start_size >= _dmalloc_alloc_total) {
529     BIT_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
530     start_size = 0;
531     /* disable this check so the interval can go on/off */
532   }
533 
534   /* checking heap every X times */
535   else if (_dmalloc_check_interval > 0) {
536     if (_dmalloc_iter_c % _dmalloc_check_interval == 0) {
537       BIT_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
538     }
539     else {
540       BIT_CLEAR(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP);
541     }
542   }
543 
544   /* after all that, do we need to check the heap? */
545   if (check_heap_b && BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP)) {
546     (void)_dmalloc_chunk_heap_check();
547   }
548 
549   return 1;
550 }
551 
552 /*
553  * Going out of the alloc routines back to user space.
554  */
dmalloc_out(void)555 static	void	dmalloc_out(void)
556 {
557   in_alloc_b = 0;
558 
559 #if LOCK_THREADS
560   unlock_thread();
561 #endif
562 
563   if (do_shutdown_b) {
564     dmalloc_shutdown();
565   }
566 }
567 
568 /***************************** exported routines *****************************/
569 
570 /*
571  * void dmalloc_shutdown
572  *
573  * Shutdown the dmalloc library and provide statistics if necessary.
574  */
dmalloc_shutdown(void)575 void	dmalloc_shutdown(void)
576 {
577   /* NOTE: do not generate errors for IN_TWICE here */
578 
579   /* if we're already in die mode leave fast and quietly */
580   if (_dmalloc_aborting_b) {
581     return;
582   }
583 
584   /*
585    * Make sure that the log file is open.  We do this here because we
586    * might cause an allocation in the open() and don't want to go
587    * recursive.
588    */
589   _dmalloc_open_log();
590 
591   /* if we've died in dmalloc somewhere then leave fast and quietly */
592   if (in_alloc_b) {
593     return;
594   }
595 
596 #if LOCK_THREADS
597   lock_thread();
598 #endif
599 
600   /* we do it again in case the lock synced the flag to true now */
601   if (in_alloc_b) {
602 #if LOCK_THREADS
603     unlock_thread();
604 #endif
605     return;
606   }
607 
608   in_alloc_b = 1;
609 
610   /*
611    * Check the heap since we are dumping info from it.  We check it
612    * when check-blank is enabled do make sure all of the areas have
613    * not been overwritten.  Thanks Randell.
614    */
615   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_HEAP)
616       || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_BLANK)
617       || BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_SHUTDOWN)) {
618     (void)_dmalloc_chunk_heap_check();
619   }
620 
621   /* dump some statistics to the logfile */
622   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_STATS)) {
623     _dmalloc_chunk_log_stats();
624   }
625 
626   /* report on non-freed pointers */
627   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_NONFREE)) {
628     _dmalloc_chunk_log_changed(0, 1, 0,
629 #if DUMP_UNFREED_SUMMARY_ONLY
630 		       0
631 #else
632 		       1
633 #endif
634 		       );
635   }
636 
637 #if LOG_PNT_TIMEVAL
638   {
639     TIMEVAL_TYPE	now;
640     char		time_buf1[64], time_buf2[64];
641     GET_TIMEVAL(now);
642     dmalloc_message("ending time = %s, elapsed since start = %s",
643 		    _dmalloc_ptimeval(&now, time_buf1, sizeof(time_buf1), 0),
644 		    _dmalloc_ptimeval(&now, time_buf2, sizeof(time_buf2), 1));
645   }
646 #else
647 #if HAVE_TIME /* NOT LOG_PNT_TIME */
648   {
649     TIME_TYPE	now;
650     char	time_buf1[64], time_buf2[64];
651     now = time(NULL);
652     dmalloc_message("ending time = %s, elapsed since start = %s",
653 		    _dmalloc_ptime(&now, time_buf1, sizeof(time_buf1), 0),
654 		    _dmalloc_ptime(&now, time_buf2, sizeof(time_buf2), 1));
655   }
656 #endif
657 #endif
658 
659   in_alloc_b = 0;
660 
661 #if LOCK_THREADS
662   unlock_thread();
663 #endif
664 
665   /* NOTE: do not set enabled_b to false here */
666 }
667 
668 #if FINI_DMALLOC
669 /*
670  * void __fini_dmalloc
671  *
672  * Automatic function to close dmalloc supported by some operating
673  * systems.  Pretty cool OS/compiler hack.  By default it is not
674  * necessary because we use atexit() and on_exit() to register the
675  * close functions which are more portable.
676  */
__fini_dmalloc(void)677 void	__fini_dmalloc(void)
678 {
679   dmalloc_shutdown();
680 }
681 #endif
682 
683 /*
684  * DMALLOC_PNT dmalloc_malloc
685  *
686  * Allocate and return a memory block of a certain size.
687  *
688  * Returns a valid pointer on success or NULL on failure.
689  *
690  * ARGUMENTS:
691  *
692  * file -> File-name or return-address of the caller.
693  *
694  * line -> Line-number of the caller.
695  *
696  * size -> Number of bytes requested.
697  *
698  * func_id -> Function-id to identify the type of call.  See
699  * dmalloc.h.
700  *
701  * alignment -> To align the new block to a certain number of bytes,
702  * set this to a value greater than 0.
703  *
704  * xalloc_b -> If set to 1 then print an error and exit if we run out
705  * of memory.
706  */
dmalloc_malloc(const char * file,const int line,const DMALLOC_SIZE size,const int func_id,const DMALLOC_SIZE alignment,const int xalloc_b)707 DMALLOC_PNT	dmalloc_malloc(const char *file, const int line,
708 			       const DMALLOC_SIZE size, const int func_id,
709 			       const DMALLOC_SIZE alignment,
710 			       const int xalloc_b)
711 {
712   void		*new_p;
713   DMALLOC_SIZE	align;
714 
715 #if DMALLOC_SIZE_UNSIGNED == 0
716   if (size < 0) {
717     dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
718     dmalloc_error("malloc");
719     if (tracking_func != NULL) {
720       tracking_func(file, line, func_id, size, alignment, NULL, NULL);
721     }
722     return MALLOC_ERROR;
723   }
724 #endif
725 
726   if (! dmalloc_in(file, line, 1)) {
727     if (tracking_func != NULL) {
728       tracking_func(file, line, func_id, size, alignment, NULL, NULL);
729     }
730     return MALLOC_ERROR;
731   }
732 
733   if (alignment == 0) {
734     if (func_id == DMALLOC_FUNC_VALLOC) {
735       align = BLOCK_SIZE;
736     }
737     else {
738       align = 0;
739     }
740   }
741   else if (alignment >= BLOCK_SIZE) {
742     align = BLOCK_SIZE;
743   }
744   else {
745     /*
746      * NOTE: Currently, there is no support in the library for
747      * memalign on less than block boundaries.  It will be non-trivial
748      * to support valloc with fence-post checking and the lack of the
749      * flag width for dblock allocations.
750      */
751     if (! memalign_warn_b) {
752       dmalloc_message("WARNING: memalign called without library support");
753       memalign_warn_b = 1;
754     }
755     align = 0;
756     /* align = alignment */
757   }
758 
759   new_p = _dmalloc_chunk_malloc(file, line, size, func_id, align);
760 
761   check_pnt(file, line, new_p, "malloc");
762 
763   dmalloc_out();
764 
765   if (tracking_func != NULL) {
766     tracking_func(file, line, func_id, size, alignment, NULL, new_p);
767   }
768 
769   if (xalloc_b && new_p == NULL) {
770     char	mess[1024], desc[128];
771     (void)loc_snprintf(mess, sizeof(mess),
772 		       "Out of memory while allocating %d bytes from '%s'\n",
773 		       size,
774 		       _dmalloc_chunk_desc_pnt(desc, sizeof(desc),
775 					       file, line));
776     (void)write(STDERR, mess, strlen(mess));
777     _exit(1);
778   }
779 
780   return new_p;
781 }
782 
783 /*
784  * DMALLOC_PNT dmalloc_realloc
785  *
786  * Resizes and old pointer to a new number of bytes.
787  *
788  * Returns a valid pointer on success or NULL on failure.
789  *
790  * ARGUMENTS:
791  *
792  * file -> File-name or return-address of the caller.
793  *
794  * line -> Line-number of the caller.
795  *
796  * old_pnt -> Pointer to an existing memory chunk that we are
797  * resizing.  If this is NULL then it basically does a malloc.
798  *
799  * new_size -> New number of bytes requested for the old pointer.
800  *
801  * func_id -> Function-id to identify the type of call.  See
802  * dmalloc.h.
803  *
804  * xalloc_b -> If set to 1 then print an error and exit if we run out
805  * of memory.
806  */
dmalloc_realloc(const char * file,const int line,DMALLOC_PNT old_pnt,DMALLOC_SIZE new_size,const int func_id,const int xalloc_b)807 DMALLOC_PNT	dmalloc_realloc(const char *file, const int line,
808 				DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size,
809 				const int func_id, const int xalloc_b)
810 {
811   void		*new_p;
812 
813 #if DMALLOC_SIZE_UNSIGNED == 0
814   if (new_size < 0) {
815     dmalloc_errno = DMALLOC_ERROR_BAD_SIZE;
816     dmalloc_error("realloc");
817     if (tracking_func != NULL) {
818       tracking_func(file, line, func_id, new_size, 0, old_pnt, NULL);
819     }
820     return MALLOC_ERROR;
821   }
822 #endif
823 
824   if (! dmalloc_in(file, line, 1)) {
825     if (tracking_func != NULL) {
826       tracking_func(file, line, func_id, new_size, 0, old_pnt, NULL);
827     }
828     return REALLOC_ERROR;
829   }
830 
831   check_pnt(file, line, old_pnt, "realloc-in");
832 
833 #if ALLOW_REALLOC_NULL
834   if (old_pnt == NULL) {
835     int		new_func_id;
836     /* shift the function id to be calloc or malloc */
837     if (func_id == DMALLOC_FUNC_RECALLOC) {
838       new_func_id = DMALLOC_FUNC_CALLOC;
839     }
840     else {
841       new_func_id = DMALLOC_FUNC_MALLOC;
842     }
843     new_p = _dmalloc_chunk_malloc(file, line, new_size, new_func_id, 0);
844   }
845   else
846 #endif
847 #if ALLOW_REALLOC_SIZE_ZERO
848     if (new_size == 0) {
849       /*
850        * If realloc(old_pnt, 0) then free(old_pnt).  Thanks to Stefan
851        * Froehlich for patiently pointing that the realloc in just
852        * about every Unix has this functionality.
853        */
854       (void)_dmalloc_chunk_free(file, line, old_pnt, func_id);
855       new_p = NULL;
856     }
857     else
858 #endif
859       new_p = _dmalloc_chunk_realloc(file, line, old_pnt, new_size, func_id);
860 
861   if (new_p != NULL) {
862     check_pnt(file, line, new_p, "realloc-out");
863   }
864 
865   dmalloc_out();
866 
867   if (tracking_func != NULL) {
868     tracking_func(file, line, func_id, new_size, 0, old_pnt, new_p);
869   }
870 
871   if (xalloc_b && new_p == NULL) {
872     char	mess[1024], desc[128];
873     (void)loc_snprintf(mess, sizeof(mess),
874 		       "Out of memory while reallocating %d bytes from '%s'\n",
875 		       new_size, _dmalloc_chunk_desc_pnt(desc, sizeof(desc),
876 							 file, line));
877     (void)write(STDERR, mess, strlen(mess));
878     _exit(1);
879   }
880 
881   return new_p;
882 }
883 
884 /*
885  * int dmalloc_free
886  *
887  * Release a pointer back into the heap.
888  *
889  * Returns FREE_NOERROR on success or FREE_ERROR on failure.
890  *
891  * Note: many operating systems define free to return (void) so this
892  * return value may be filtered.  Dumb.
893  *
894  * ARGUMENTS:
895  *
896  * file -> File-name or return-address of the caller.
897  *
898  * line -> Line-number of the caller.
899  *
900  * pnt -> Existing pointer we are freeing.
901  *
902  * func_id -> Function-id to identify the type of call.  See
903  * dmalloc.h.
904  */
dmalloc_free(const char * file,const int line,DMALLOC_PNT pnt,const int func_id)905 int	dmalloc_free(const char *file, const int line, DMALLOC_PNT pnt,
906 		     const int func_id)
907 {
908   int		ret;
909 
910   if (! dmalloc_in(file, line, 1)) {
911     if (tracking_func != NULL) {
912       tracking_func(file, line, func_id, 0, 0, pnt, NULL);
913     }
914     return FREE_ERROR;
915   }
916 
917   check_pnt(file, line, pnt, "free");
918 
919   ret = _dmalloc_chunk_free(file, line, pnt, func_id);
920 
921   dmalloc_out();
922 
923   if (tracking_func != NULL) {
924     tracking_func(file, line, DMALLOC_FUNC_FREE, 0, 0, pnt, NULL);
925   }
926 
927   return ret;
928 }
929 
930 /*
931  * DMALLOC_PNT dmalloc_strndup
932  *
933  * Allocate and return an allocated block of memory holding a copy of
934  * a string of a certain number of characters.
935  *
936  * Returns a valid pointer on success or NULL on failure.
937  *
938  * ARGUMENTS:
939  *
940  * file -> File-name or return-address of the caller.
941  *
942  * line -> Line-number of the caller.
943  *
944  * string -> String we are duplicating.
945  *
946  * max_len -> Max length of the string we are duplicating.  Set to -1 for none.
947  *
948  * xalloc_b -> If set to 1 then print an error and exit if we run out
949  * of memory.
950  */
dmalloc_strndup(const char * file,const int line,const char * string,const int max_len,const int xalloc_b)951 char	*dmalloc_strndup(const char *file, const int line,
952 			 const char *string, const int max_len,
953 			 const int xalloc_b)
954 {
955   DMALLOC_SIZE	size;
956   char		*new_string;
957   const char	*string_p;
958 
959   /* check the arguments */
960   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_FUNCS)) {
961     /* we check for pointer plus \0 */
962     if (! dmalloc_verify_pnt_strsize(file, line, "strndup", string,
963 				     0 /* not exact */, 1 /* strlen */,
964 				     (max_len < 0 ? 0 : max_len))) {
965       dmalloc_message("bad pointer argument found in strdup");
966     }
967   }
968 
969   /* so we have to figure out the max length of the string directly */
970   if (max_len < 0) {
971     size = strlen(string);
972   }
973   else {
974     for (string_p = string; string_p < string + max_len; string_p++) {
975       if (*string_p == '\0') {
976 	break;
977       }
978     }
979     size = string_p - string;
980   }
981 
982   /* allocate space for the \0 */
983   new_string = dmalloc_malloc(file, line, size + 1, DMALLOC_FUNC_STRDUP,
984 			      0 /* no alignment */, xalloc_b);
985   if (new_string != NULL) {
986     strncpy(new_string, string, size);
987     new_string[size] = '\0';
988   }
989 
990   return new_string;
991 }
992 
993 /*************************** external memory calls ***************************/
994 
995 /*
996  * DMALLOC_PNT malloc
997  *
998  * Overloading the malloc(3) function.  Allocate and return a memory
999  * block of a certain size.
1000  *
1001  * Returns a valid pointer on success or NULL on failure.
1002  *
1003  * ARGUMENTS:
1004  *
1005  * size -> Number of bytes requested.
1006  */
1007 #undef malloc
malloc(DMALLOC_SIZE size)1008 DMALLOC_PNT	malloc(DMALLOC_SIZE size)
1009 {
1010   char	*file;
1011 
1012   GET_RET_ADDR(file);
1013   return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size, DMALLOC_FUNC_MALLOC,
1014 			0 /* no alignment */, 0 /* no xalloc messages */);
1015 }
1016 
1017 /*
1018  * DMALLOC_PNT calloc
1019  *
1020  * Overloading the calloc(3) function.  Returns a block of zeroed memory.
1021  *
1022  * Returns a valid pointer on success or NULL on failure.
1023  *
1024  * ARGUMENTS:
1025  *
1026  * num_elements -> Number of elements being allocated.
1027  *
1028  * size -> The number of bytes in each element.
1029  */
1030 #undef calloc
calloc(DMALLOC_SIZE num_elements,DMALLOC_SIZE size)1031 DMALLOC_PNT	calloc(DMALLOC_SIZE num_elements, DMALLOC_SIZE size)
1032 {
1033   DMALLOC_SIZE	len = num_elements * size;
1034   char		*file;
1035 
1036   GET_RET_ADDR(file);
1037   return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, len, DMALLOC_FUNC_CALLOC,
1038 			0 /* no alignment */, 0 /* no xalloc messages */);
1039 }
1040 
1041 /*
1042  * DMALLOC_PNT realloc
1043  *
1044  * Overload of realloc(3).  Resizes and old pointer to a new number of bytes.
1045  *
1046  * Returns a valid pointer on success or NULL on failure.
1047  *
1048  * ARGUMENTS:
1049  *
1050  * old_pnt -> Pointer to an existing memory chunk that we are
1051  * resizing.  If this is NULL then it basically does a malloc.
1052  *
1053  * new_size -> New number of bytes requested for the old pointer.
1054  */
1055 #undef realloc
realloc(DMALLOC_PNT old_pnt,DMALLOC_SIZE new_size)1056 DMALLOC_PNT	realloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size)
1057 {
1058   char	*file;
1059 
1060   GET_RET_ADDR(file);
1061   return dmalloc_realloc(file, DMALLOC_DEFAULT_LINE, old_pnt, new_size,
1062 			 DMALLOC_FUNC_REALLOC, 0 /* no xalloc messages */);
1063 }
1064 
1065 /*
1066  * DMALLOC_PNT recalloc
1067  *
1068  * Overload of recalloc(3) which exists on some systems.  Resizes and
1069  * old pointer to a new number of bytes.  If we are expanding, then
1070  * any new bytes will be zeroed.
1071  *
1072  * Returns a valid pointer on success or NULL on failure.
1073  *
1074  * ARGUMENTS:
1075  *
1076  * old_pnt -> Pointer to an existing memory chunk that we are resizing.
1077  *
1078  * new_size -> New number of bytes requested for the old pointer.
1079  */
1080 #undef recalloc
recalloc(DMALLOC_PNT old_pnt,DMALLOC_SIZE new_size)1081 DMALLOC_PNT	recalloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size)
1082 {
1083   char	*file;
1084 
1085   GET_RET_ADDR(file);
1086   return dmalloc_realloc(file, DMALLOC_DEFAULT_LINE, old_pnt, new_size,
1087 			 DMALLOC_FUNC_RECALLOC, 0 /* no xalloc messages */);
1088 }
1089 
1090 /*
1091  * DMALLOC_PNT memalign
1092  *
1093  * Overloading the memalign(3) function.  Allocate and return a memory
1094  * block of a certain size which have been aligned to a certain
1095  * alignment.
1096  *
1097  * Returns a valid pointer on success or NULL on failure.
1098  *
1099  * ARGUMENTS:
1100  *
1101  * alignment -> Value to which the allocation must be aligned.  This
1102  * should probably be a multiple of 2 with a maximum value equivalent
1103  * to the block-size which is often 1k or 4k.
1104  *
1105  * size -> Number of bytes requested.
1106  */
1107 #undef memalign
memalign(DMALLOC_SIZE alignment,DMALLOC_SIZE size)1108 DMALLOC_PNT	memalign(DMALLOC_SIZE alignment, DMALLOC_SIZE size)
1109 {
1110   char		*file;
1111 
1112   GET_RET_ADDR(file);
1113   return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size,
1114 			DMALLOC_FUNC_MEMALIGN, alignment,
1115 			0 /* no xalloc messages */);
1116 }
1117 
1118 /*
1119  * DMALLOC_PNT valloc
1120  *
1121  * Overloading the valloc(3) function.  Allocate and return a memory
1122  * block of a certain size which have been aligned to page boundaries
1123  * which are often 1k or 4k.
1124  *
1125  * Returns a valid pointer on success or NULL on failure.
1126  *
1127  * ARGUMENTS:
1128  *
1129  * size -> Number of bytes requested.
1130  */
1131 #undef valloc
valloc(DMALLOC_SIZE size)1132 DMALLOC_PNT	valloc(DMALLOC_SIZE size)
1133 {
1134   char	*file;
1135 
1136   GET_RET_ADDR(file);
1137   return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size, DMALLOC_FUNC_VALLOC,
1138 			BLOCK_SIZE, 0 /* no xalloc messages */);
1139 }
1140 
1141 #ifndef DMALLOC_STRDUP_MACRO
1142 /*
1143  * DMALLOC_PNT strdup
1144  *
1145  * Overload of strdup(3).  Allocate and return an allocated block of
1146  * memory holding a copy of a string.
1147  *
1148  * Returns a valid pointer on success or NULL on failure.
1149  *
1150  * ARGUMENTS:
1151  *
1152  * string -> String we are duplicating.
1153  */
1154 #undef strdup
strdup(const char * string)1155 char	*strdup(const char *string)
1156 {
1157   int	len;
1158   char	*buf, *file;
1159 
1160   GET_RET_ADDR(file);
1161 
1162   /* check the arguments */
1163   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_FUNCS)) {
1164     if (! dmalloc_verify_pnt(file, 0 /* no line */, "strdup", string,
1165 			     0 /* not exact */, -1)) {
1166       dmalloc_message("bad pointer argument found in strdup");
1167     }
1168   }
1169 
1170   /* len + \0 */
1171   len = strlen(string) + 1;
1172 
1173   buf = dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, len, DMALLOC_FUNC_STRDUP,
1174 		       0 /* no alignment */, 0 /* no xalloc messages */);
1175   if (buf != NULL) {
1176     (void)memcpy(buf, string, len);
1177   }
1178 
1179   return buf;
1180 }
1181 #endif /* ifndef DMALLOC_STRDUP_MACRO */
1182 
1183 #ifndef DMALLOC_STRNDUP_MACRO
1184 /*
1185  * DMALLOC_PNT strndup
1186  *
1187  * Overload of strndup(3).  Allocate and return an allocated block of
1188  * memory holding a copy of a string with a maximum length.
1189  *
1190  * Returns a valid pointer on success or NULL on failure.
1191  *
1192  * ARGUMENTS:
1193  *
1194  * string -> String we are duplicating.
1195  *
1196  * max_len -> Max length of the string to duplicate.
1197  */
1198 #undef strndup
strndup(const char * string,const DMALLOC_SIZE max_len)1199 char	*strndup(const char *string, const DMALLOC_SIZE max_len)
1200 {
1201   int		size;
1202   char		*buf, *file;
1203   const char	*string_p;
1204 
1205   GET_RET_ADDR(file);
1206 
1207   /* check the arguments */
1208   if (BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_CHECK_FUNCS)) {
1209     if (! dmalloc_verify_pnt_strsize(file, 0 /* no line */, "strndup", string,
1210 				     0 /* not exact */, 1 /* strlen */, max_len)) {
1211       dmalloc_message("bad pointer argument found in strdup");
1212     }
1213   }
1214 
1215   /* so we have to figure out the max length of the string directly */
1216   for (string_p = string; string_p < string + max_len; string_p++) {
1217     if (*string_p == '\0') {
1218       break;
1219     }
1220   }
1221   size = string_p - string;
1222 
1223   /* at 1 for null */
1224   buf = dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size + 1,
1225 		       DMALLOC_FUNC_STRDUP, 0 /* no alignment */,
1226 		       0 /* no xalloc messages */);
1227   if (buf != NULL) {
1228     (void)memcpy(buf, string, size);
1229     buf[size] = '\0';
1230   }
1231 
1232   return buf;
1233 }
1234 #endif /* ifndef DMALLOC_STRNDUP_MACRO */
1235 
1236 /*
1237  * DMALLOC_FREE_RET free
1238  *
1239  * Release a pointer back into the heap.
1240  *
1241  * Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC
1242  * is defined by your compiler.
1243  *
1244  * ARGUMENTS:
1245  *
1246  * pnt -> Existing pointer we are freeing.
1247  */
1248 #undef free
free(DMALLOC_PNT pnt)1249 DMALLOC_FREE_RET	free(DMALLOC_PNT pnt)
1250 {
1251   char	*file;
1252 #ifdef DMALLOC_FREE_RET_INT
1253   int	ret;
1254 #endif
1255 
1256   GET_RET_ADDR(file);
1257 #ifdef DMALLOC_FREE_RET_INT
1258   ret =
1259 #endif
1260     dmalloc_free(file, DMALLOC_DEFAULT_LINE, pnt, DMALLOC_FUNC_FREE);
1261 
1262 #ifdef DMALLOC_FREE_RET_INT
1263   return ret;
1264 #endif
1265 }
1266 
1267 /*
1268  * DMALLOC_FREE_RET cfree
1269  *
1270  * Same as free.
1271  *
1272  * Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC
1273  * is defined by your compiler.
1274  *
1275  * ARGUMENTS:
1276  *
1277  * pnt -> Existing pointer we are freeing.
1278  */
1279 #undef cfree
cfree(DMALLOC_PNT pnt)1280 DMALLOC_FREE_RET	cfree(DMALLOC_PNT pnt)
1281 {
1282   char	*file;
1283 #ifdef DMALLOC_FREE_RET_INT
1284   int	ret;
1285 #endif
1286 
1287   GET_RET_ADDR(file);
1288 #ifdef DMALLOC_FREE_RET_INT
1289   ret =
1290 #endif
1291     dmalloc_free(file, DMALLOC_DEFAULT_LINE, pnt, DMALLOC_FUNC_CFREE);
1292 
1293 #ifdef DMALLOC_FREE_RET_INT
1294   return ret;
1295 #endif
1296 }
1297 
1298 /******************************* utility calls *******************************/
1299 
1300 /*
1301  * int dmalloc_verify
1302  *
1303  * Verify a pointer which has previously been allocated by the
1304  * library or check the entire heap.
1305  *
1306  * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure.
1307  *
1308  * ARGUMENTS:
1309  *
1310  * pnt -> Pointer we are verifying.  If 0L then check the entire heap.
1311  */
dmalloc_verify(const DMALLOC_PNT pnt)1312 int	dmalloc_verify(const DMALLOC_PNT pnt)
1313 {
1314   int	ret;
1315 
1316   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 0)) {
1317     return MALLOC_VERIFY_NOERROR;
1318   }
1319 
1320   /* should not check heap here because we will be doing it below */
1321 
1322   if (pnt == NULL) {
1323     ret = _dmalloc_chunk_heap_check();
1324   }
1325   else {
1326     ret = _dmalloc_chunk_pnt_check("dmalloc_verify", pnt,
1327 				   1 /* exact pointer */, 0 /* no strlen */,
1328 				   0 /* no min size */);
1329   }
1330 
1331   dmalloc_out();
1332 
1333   if (ret) {
1334     return MALLOC_VERIFY_NOERROR;
1335   }
1336   else {
1337     return MALLOC_VERIFY_ERROR;
1338   }
1339 }
1340 
1341 /*
1342  * int malloc_verify
1343  *
1344  * Verify a pointer which has previously been allocated by the
1345  * library.  Same as dmalloc_verify.
1346  *
1347  * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure.
1348  *
1349  * ARGUMENTS:
1350  *
1351  * pnt -> Pointer we are verifying.  If 0L then check the entire heap.
1352  */
malloc_verify(const DMALLOC_PNT pnt)1353 int	malloc_verify(const DMALLOC_PNT pnt)
1354 {
1355   return dmalloc_verify(pnt);
1356 }
1357 
1358 /*
1359  * int dmalloc_verify_pnt
1360  *
1361  * This function is mainly used by the arg_check.c functions to verify
1362  * specific pointers.  This can be used by users to provide more fine
1363  * grained tests on pointers.
1364  *
1365  * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure.
1366  *
1367  * ARGUMENTS:
1368  *
1369  * file -> File-name or return-address of the caller.  You can use
1370  * __FILE__ for this argument or 0L for none.
1371  *
1372  * line -> Line-number of the caller.  You can use __LINE__ for this
1373  * argument or 0 for none.
1374  *
1375  * func -> Function string which is checking the pointer.  0L if none.
1376  *
1377  * pnt -> Pointer we are checking.
1378  *
1379  * exact_b -> Set to 1 if this pointer was definitely handed back from
1380  * a memory allocation.  If set to 0 then this pointer can be inside
1381  * another allocation or outside the heap altogether.
1382  *
1383  * strlen_b -> Set to 1 to make sure that this pointer can handle
1384  * strlen(pnt) + 1 bytes up to the maximum specified by min_size.  If
1385  * this is 1 and min_size > 0 then it is in effect a strnlen.
1386  *
1387  * min_size -> Make sure that pointer can hold at least that many
1388  * bytes if inside of the heap.  If 0 then don't check the size.
1389  */
dmalloc_verify_pnt_strsize(const char * file,const int line,const char * func,const void * pnt,const int exact_b,const int strlen_b,const int min_size)1390 int	dmalloc_verify_pnt_strsize(const char *file, const int line,
1391 				   const char *func, const void *pnt,
1392 				   const int exact_b, const int strlen_b,
1393 				   const int min_size)
1394 {
1395   int	ret;
1396 
1397   if (! dmalloc_in(file, line, 0)) {
1398     return MALLOC_VERIFY_NOERROR;
1399   }
1400 
1401   /* call the pnt checking chunk code */
1402   ret = _dmalloc_chunk_pnt_check(func, pnt, exact_b, strlen_b, min_size);
1403   dmalloc_out();
1404 
1405   if (ret) {
1406     return MALLOC_VERIFY_NOERROR;
1407   }
1408   else {
1409     return MALLOC_VERIFY_ERROR;
1410   }
1411 }
1412 
1413 /*
1414  * int dmalloc_verify_pnt
1415  *
1416  * This function is mainly used by the arg_check.c functions to verify
1417  * specific pointers.  This can be used by users to provide more fine
1418  * grained tests on pointers.
1419  *
1420  * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure.
1421  *
1422  * ARGUMENTS:
1423  *
1424  * file -> File-name or return-address of the caller.  You can use
1425  * __FILE__ for this argument or 0L for none.
1426  *
1427  * line -> Line-number of the caller.  You can use __LINE__ for this
1428  * argument or 0 for none.
1429  *
1430  * func -> Function string which is checking the pointer.  0L if none.
1431  *
1432  * pnt -> Pointer we are checking.
1433  *
1434  * exact_b -> Set to 1 if this pointer was definitely handed back from
1435  * a memory allocation.  If set to 0 then this pointer can be inside
1436  * another allocation or outside the heap altogether.
1437  *
1438  * min_size -> Make sure that pointer can hold at least that many
1439  * bytes if inside of the heap.  If -1 then make sure it can handle
1440  * strlen(pnt) + 1 bytes (+1 for the \0).  If 0 then don't check the
1441  * size.  If you need strnlen functionality with a maximum on the
1442  * strlen, see dmalloc_verify_pnt_strsize.
1443  */
dmalloc_verify_pnt(const char * file,const int line,const char * func,const void * pnt,const int exact_b,const int min_size)1444 int	dmalloc_verify_pnt(const char *file, const int line, const char *func,
1445 			   const void *pnt, const int exact_b,
1446 			   const int min_size)
1447 {
1448   if (min_size < 0) {
1449     return dmalloc_verify_pnt_strsize(file, line, func, pnt, exact_b,
1450 				      1 /* strlen */, 0 /* no min-size */);
1451   } else {
1452     return dmalloc_verify_pnt_strsize(file, line, func, pnt, exact_b,
1453 				      0 /* no strlen */, min_size);
1454   }
1455 }
1456 
1457 /*
1458  * unsigned int dmalloc_debug
1459  *
1460  * Set the global debug functionality flags.  You can also use
1461  * dmalloc_debug_setup.
1462  *
1463  * Note: you cannot add or remove certain flags such as signal
1464  * handlers since they are setup at initialization time only.
1465  *
1466  * Returns the old debug flag value.
1467  *
1468  * ARGUMENTS:
1469  *
1470  * flags -> Flag value to set.  Pass in 0 to disable all debugging.
1471  */
dmalloc_debug(const unsigned int flags)1472 unsigned int	dmalloc_debug(const unsigned int flags)
1473 {
1474   unsigned int	old_flags;
1475 
1476   if (! enabled_b) {
1477     (void)dmalloc_startup(NULL /* no options string */);
1478   }
1479 
1480   old_flags = _dmalloc_flags;
1481 
1482   /* add the new flags */
1483   _dmalloc_flags = flags;
1484 
1485   return old_flags;
1486 }
1487 
1488 /*
1489  * char *dmalloc_debug_current
1490  *
1491  * Returns the current debug functionality flags.  This allows you to
1492  * save a dmalloc library state to be restored later.
1493  */
dmalloc_debug_current(void)1494 unsigned int	dmalloc_debug_current(void)
1495 {
1496   if (! enabled_b) {
1497     (void)dmalloc_startup(NULL /* no options string */);
1498   }
1499 
1500   /* should not check the heap here since we are dumping the debug variable */
1501   return _dmalloc_flags;
1502 }
1503 
1504 /*
1505  * char *dmalloc_debug_current_env
1506  *
1507  * Returns the current debug environment.  This allows you to save a
1508  * dmalloc library state to be restored later with a call to
1509  * dmalloc_debug_setup().
1510  *
1511  * Returns the current debug environment.
1512  *
1513  * ARGUMENTS:
1514  *
1515  * env_buf -> Buffer to use for getting the environment.
1516  *
1517  * env_buf_size -> Size of the buffer.
1518  */
dmalloc_debug_current_env(char * env_buf,const int env_buf_size)1519 char	*dmalloc_debug_current_env(char *env_buf, const int env_buf_size)
1520 {
1521   if (! enabled_b) {
1522     (void)dmalloc_startup(NULL /* no options string */);
1523   }
1524   return loc_getenv(OPTIONS_ENVIRON, env_buf, env_buf_size, 0);
1525 }
1526 
1527 /*
1528  * void dmalloc_debug_setup
1529  *
1530  * Set the global debugging functionality as an option string.
1531  * Normally this would be pased in in the DMALLOC_OPTIONS
1532  * environmental variable.  This is here to override the env or for
1533  * circumstances where modifying the environment is not possible or
1534  * does not apply such as servers or cgi-bin programs.
1535  *
1536  * ARGUMENTS:
1537  *
1538  * options_str -> Options string to set the library flags.
1539  */
dmalloc_debug_setup(const char * options_str)1540 void	dmalloc_debug_setup(const char *options_str)
1541 {
1542   if (! enabled_b) {
1543     (void)dmalloc_startup(options_str);
1544     /* if we just started up then we don't have to do anything else */
1545     return;
1546   }
1547 
1548   /* we need to lock */
1549   if (! dmalloc_in(NULL /* no file-name */, 0 /* no line-number */,
1550 		   0 /* don't-check-heap */)) {
1551     return;
1552   }
1553 
1554   process_environ(options_str);
1555   dmalloc_out();
1556 }
1557 
1558 /*
1559  * int dmalloc_examine
1560  *
1561  * Examine a pointer and pass back information on its allocation size
1562  * as well as the file and line-number where it was allocated.  If the
1563  * file and line number is not available, then it will pass back the
1564  * allocation location's return-address if available.
1565  *
1566  * Returns DMALLOC_NOERROR on success or DMALLOC_ERROR on failure.
1567  *
1568  * ARGUMENTS:
1569  *
1570  * pnt -> Pointer we are checking.
1571  *
1572  * user_size_p <- Pointer to a DMALLOC_SIZE type variable which, if
1573  * not NULL, will be set to the size of bytes from the pointer.
1574  *
1575  * total_size_p <- Poiner to a DMALLOC_SIZE type variable which, if
1576  * not NULL, will be set to the total size given for this allocation
1577  * including administrative overhead.
1578  *
1579  * file_p <- Pointer to a character pointer which, if not NULL, will
1580  * be set to the file where the pointer was allocated.
1581  *
1582  * line_p <- Pointer to an unsigned integer which, if not NULL, will
1583  * be set to the line-number where the pointer was allocated.
1584  *
1585  * ret_attr_p <- Pointer to a void pointer, if not NULL, will be set
1586  * to the return-address where the pointer was allocated.
1587  *
1588  * used_mark_p <- Poiner to an unsigned integer which, if not NULL,
1589  * will be set to the mark of when the pointer was last "used".  This
1590  * could be when it was allocated, reallocated, or freed.
1591  *
1592  * seen_p <- Poiner to an unsigned long which, if not NULL, will be
1593  * set to the number of times that this pointer has been allocated,
1594  * realloced, or freed.  NOTE: LOG_PNT_SEEN_COUNT must be set to 1
1595  * otherwise no seen information is available and it will be set to 0.
1596  */
dmalloc_examine(const DMALLOC_PNT pnt,DMALLOC_SIZE * user_size_p,DMALLOC_SIZE * total_size_p,char ** file_p,unsigned int * line_p,DMALLOC_PNT * ret_attr_p,unsigned long * used_mark_p,unsigned long * seen_p)1597 int	dmalloc_examine(const DMALLOC_PNT pnt, DMALLOC_SIZE *user_size_p,
1598 			DMALLOC_SIZE *total_size_p, char **file_p,
1599 			unsigned int *line_p, DMALLOC_PNT *ret_attr_p,
1600 			unsigned long *used_mark_p, unsigned long *seen_p)
1601 {
1602   int		ret;
1603   unsigned int	user_size_map, tot_size_map;
1604   unsigned long	*loc_seen_p;
1605 
1606   /*
1607    * NOTE: we use the size maps because we use a unsigned int size
1608    * type internally but may use some size_t externally.
1609    */
1610 
1611   /* need to check the heap here since we are geting info from it below */
1612   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
1613     return DMALLOC_ERROR;
1614   }
1615 
1616   /* NOTE: we do not need the alloc-size info */
1617   ret = _dmalloc_chunk_read_info(pnt, "dmalloc_examine", &user_size_map,
1618 				 &tot_size_map, file_p, line_p, ret_attr_p,
1619 				 &loc_seen_p, used_mark_p, NULL, NULL);
1620 
1621   dmalloc_out();
1622 
1623   if (ret) {
1624     SET_POINTER(user_size_p, user_size_map);
1625     SET_POINTER(total_size_p, tot_size_map);
1626     if (loc_seen_p == NULL) {
1627       SET_POINTER(seen_p, 0);
1628     } else {
1629       SET_POINTER(seen_p, *loc_seen_p);
1630     }
1631     return DMALLOC_NOERROR;
1632   }
1633   else {
1634     return DMALLOC_ERROR;
1635   }
1636 }
1637 
1638 /*
1639  * void dmalloc_track
1640  *
1641  * Register an allocation tracking function which will be called each
1642  * time an allocation occurs.
1643  *
1644  * ARGUMENTS:
1645  *
1646  * track_func -> Function to register as the tracking function.  Set
1647  * to NULL to disable.
1648  */
dmalloc_track(const dmalloc_track_t track_func)1649 void	dmalloc_track(const dmalloc_track_t track_func)
1650 {
1651   tracking_func = track_func;
1652 }
1653 
1654 /*
1655  * unsigned long dmalloc_mark
1656  *
1657  * Return to the caller the current "mark" which can be used later by
1658  * dmalloc_log_changed to log the changed pointers since this point.
1659  * Multiple marks can be saved and used.
1660  *
1661  * This is also the iteration number and can be logged at the front of
1662  * each memory transaction in the logfile with the LOG_ITERATION
1663  * define in settings.h and can be logged with each pointer with the
1664  * LOG_PNT_ITERATION define in settings.h.
1665  */
dmalloc_mark(void)1666 unsigned long	dmalloc_mark(void)
1667 {
1668   if (! enabled_b) {
1669     (void)dmalloc_startup(NULL /* no options string */);
1670   }
1671 
1672   return _dmalloc_iter_c;
1673 }
1674 
1675 /*
1676  * unsigned long dmalloc_memory_allocated
1677  *
1678  * Return the total number of bytes allocated by the program so far.
1679  */
dmalloc_memory_allocated(void)1680 unsigned long	dmalloc_memory_allocated(void)
1681 {
1682   if (! enabled_b) {
1683     (void)dmalloc_startup(NULL /* no options string */);
1684   }
1685 
1686   return _dmalloc_alloc_total;
1687 }
1688 
1689 /*
1690  * unsigned int dmalloc_page_size
1691  *
1692  * Get the page-size being used by dmalloc.
1693  */
dmalloc_page_size(void)1694 unsigned int	dmalloc_page_size(void)
1695 {
1696   if (! enabled_b) {
1697     (void)dmalloc_startup(NULL /* no options string */);
1698   }
1699 
1700   return BLOCK_SIZE;
1701 }
1702 
1703 /*
1704  * unsigned long dmalloc_count_changed
1705  *
1706  * Count the changed memory bytes since a particular mark.
1707  *
1708  * Returns the number of bytes since mark.
1709  *
1710  * ARGUMENTS:
1711  *
1712  * mark -> Sets the point from which to count the changed memory.  You
1713  * can use dmalloc_mark to get the current mark value which can later
1714  * be passed in here.  Pass in 0 to report on the unfreed memory since
1715  * the program started.
1716  *
1717  * not_freed_b -> Set to 1 to count the new pointers that are non-freed.
1718  *
1719  * free_b -> Set to 1 to count the new pointers that are freed.
1720  */
dmalloc_count_changed(const unsigned long mark,const int not_freed_b,const int free_b)1721 unsigned long	dmalloc_count_changed(const unsigned long mark,
1722 				      const int not_freed_b, const int free_b)
1723 {
1724   unsigned long	mem_count;
1725 
1726   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
1727     return 0;
1728   }
1729 
1730   if (! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
1731     dmalloc_message("counting the unfreed memory since mark %lu", mark);
1732   }
1733 
1734   mem_count = _dmalloc_chunk_count_changed(mark, not_freed_b, free_b);
1735 
1736   dmalloc_out();
1737 
1738   return mem_count;
1739 }
1740 
1741 /*
1742  * void dmalloc_log_status
1743  *
1744  * Dump dmalloc statistics to logfile.
1745  */
dmalloc_log_stats(void)1746 void	dmalloc_log_stats(void)
1747 {
1748   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
1749     return;
1750   }
1751 
1752   _dmalloc_chunk_log_stats();
1753 
1754   dmalloc_out();
1755 }
1756 
1757 /*
1758  * void dmalloc_log_unfreed
1759  *
1760  * Dump unfreed-memory info to logfile.
1761  */
dmalloc_log_unfreed(void)1762 void	dmalloc_log_unfreed(void)
1763 {
1764   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
1765     return;
1766   }
1767 
1768   if (! BIT_IS_SET(_dmalloc_flags, DMALLOC_DEBUG_LOG_TRANS)) {
1769     dmalloc_message("dumping the unfreed pointers");
1770   }
1771 
1772   /*
1773    * to log the non-free we are interested in the pointers currently
1774    * being used
1775    */
1776   _dmalloc_chunk_log_changed(0, 1, 0,
1777 #if DUMP_UNFREED_SUMMARY_ONLY
1778 			0
1779 #else
1780 			1
1781 #endif
1782 			);
1783 
1784   dmalloc_out();
1785 }
1786 
1787 /*
1788  * void dmalloc_log_changed
1789  *
1790  * Dump the pointers that have changed since a point in time.
1791  *
1792  * ARGUMENTS:
1793  *
1794  * mark -> Sets the point to compare against.  You can use
1795  * dmalloc_mark to get the current mark value which can later be
1796  * passed in here.  Pass in 0 to log what has changed since the
1797  * program started.
1798  *
1799  * not_freed_b -> Set to 1 to log the new pointers that are non-freed.
1800  *
1801  * free_b -> Set to 1 to log the new pointers that are freed.
1802  *
1803  * details_b -> Set to 1 to dump the individual pointers that have
1804  * changed otherwise the summaries will be logged.
1805  */
dmalloc_log_changed(const unsigned long mark,const int not_freed_b,const int free_b,const int details_b)1806 void	dmalloc_log_changed(const unsigned long mark, const int not_freed_b,
1807 			    const int free_b, const int details_b)
1808 {
1809   if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
1810     return;
1811   }
1812   _dmalloc_chunk_log_changed(mark, not_freed_b, free_b, details_b);
1813 
1814   dmalloc_out();
1815 }
1816 
1817 /*
1818  * void dmalloc_vmessage
1819  *
1820  * Message writer with vprintf like arguments which adds a line to the
1821  * dmalloc logfile.
1822  *
1823  * ARGUMENTS:
1824  *
1825  * format -> Printf-style format statement.
1826  *
1827  * args -> Already converted pointer to a stdarg list.
1828  */
dmalloc_vmessage(const char * format,va_list args)1829 void	dmalloc_vmessage(const char *format, va_list args)
1830 {
1831   _dmalloc_vmessage(format, args);
1832 }
1833 
1834 /*
1835  * void dmalloc_message
1836  *
1837  * Message writer with printf like arguments which adds a line to the
1838  * dmalloc logfile.
1839  *
1840  * ARGUMENTS:
1841  *
1842  * format -> Printf-style format statement.
1843  *
1844  * ... -> Variable argument list.
1845  */
dmalloc_message(const char * format,...)1846 void	dmalloc_message(const char *format, ...)
1847   /* __attribute__ ((format (printf, 1, 2))) */
1848 {
1849   va_list	args;
1850 
1851   va_start(args, format);
1852   _dmalloc_vmessage(format, args);
1853   va_end(args);
1854 }
1855 
1856 /*
1857  * void dmalloc_get_stats
1858  *
1859  * Get a number of statistics about the current heap.
1860  *
1861  * ARGUMENTS:
1862  *
1863  * heap_low_p <- Pointer to pointer which, if not 0L, will be set to
1864  * the low address in the heap.
1865  *
1866  * heap_high_p <- Pointer to pointer which, if not 0L, will be set to
1867  * the high address in the heap.
1868  *
1869  * total_space_p <- Pointer to an unsigned long which, if not 0L, will
1870  * be set to the total space managed by the library including user
1871  * space, administrative space, and overhead.
1872  *
1873  * user_space_p <- Pointer to an unsigned long which, if not 0L, will
1874  * be set to the space given to the user process (allocated and free).
1875  *
1876  * current_allocated_p <- Pointer to an unsigned long which, if not
1877  * 0L, will be set to the current allocated space given to the user
1878  * process.
1879  *
1880  * current_pnt_np <- Pointer to an unsigned long which, if not 0L,
1881  * will be set to the current number of pointers allocated by the user
1882  * process.
1883  *
1884  * max_allocated_p <- Pointer to an unsigned long which, if not 0L,
1885  * will be set to the maximum allocated space given to the user
1886  * process.
1887  *
1888  * max_pnt_np <- Pointer to an unsigned long which, if not 0L, will be
1889  * set to the maximum number of pointers allocated by the user
1890  * process.
1891  *
1892  * max_one_p <- Pointer to an unsigned long which, if not 0L, will be
1893  * set to the maximum allocated with 1 call by the user process.
1894  */
dmalloc_get_stats(DMALLOC_PNT * heap_low_p,DMALLOC_PNT * 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)1895 void	dmalloc_get_stats(DMALLOC_PNT *heap_low_p,
1896 			  DMALLOC_PNT *heap_high_p,
1897 			  unsigned long *total_space_p,
1898 			  unsigned long *user_space_p,
1899 			  unsigned long *current_allocated_p,
1900 			  unsigned long *current_pnt_np,
1901 			  unsigned long *max_allocated_p,
1902 			  unsigned long *max_pnt_np,
1903 			  unsigned long *max_one_p)
1904 {
1905   _dmalloc_chunk_get_stats(heap_low_p, heap_high_p, total_space_p,
1906 			   user_space_p, current_allocated_p, current_pnt_np,
1907 			   max_allocated_p, max_pnt_np, max_one_p);
1908 }
1909 
1910 /*
1911  * const char *dmalloc_strerror
1912  *
1913  * Convert a dmalloc error code into its string equivalent.
1914  *
1915  * Returns String version of the error on success or "unknown error" on failure.
1916  *
1917  * ARGUMENTS:
1918  *
1919  * error_num -> Error number we are converting.
1920  */
dmalloc_strerror(const int error_num)1921 const char	*dmalloc_strerror(const int error_num)
1922 {
1923   error_str_t	*err_p;
1924 
1925   /* should not dmalloc_in here because _dmalloc_error calls this */
1926 
1927   for (err_p = error_list; err_p->es_error != 0; err_p++) {
1928     if (err_p->es_error == error_num) {
1929       return err_p->es_string;
1930     }
1931   }
1932 
1933   return INVALID_ERROR;
1934 }
1935