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