1 /*
2 ** MEMWATCH.C
3 ** Nonintrusive ANSI C memory leak / overwrite detection
4 ** Copyright (C) 1992-99 Johan Lindh
5 ** All rights reserved.
6 ** Version 2.64
7 **
8 ** 920810 JLI   [1.00]
9 ** 920830 JLI   [1.10 double-free detection]
10 ** 920912 JLI   [1.15 mwPuts, mwGrab/Drop, mwLimit]
11 ** 921022 JLI   [1.20 ASSERT and VERIFY]
12 ** 921105 JLI   [1.30 C++ support and TRACE]
13 ** 921116 JLI   [1.40 mwSetOutFunc]
14 ** 930215 JLI   [1.50 modified ASSERT/VERIFY]
15 ** 930327 JLI   [1.51 better auto-init & PC-lint support]
16 ** 930506 JLI   [1.55 MemWatch class, improved C++ support]
17 ** 930507 JLI   [1.60 mwTest & CHECK()]
18 ** 930809 JLI   [1.65 Abort/Retry/Ignore]
19 ** 930820 JLI   [1.70 data dump when unfreed]
20 ** 931016 JLI   [1.72 modified C++ new/delete handling]
21 ** 931108 JLI   [1.77 mwSetAssertAction() & some small changes]
22 ** 940110 JLI   [1.80 no-mans-land alloc/checking]
23 ** 940328 JLI   [2.00 version 2.0 rewrite]
24 **              Improved NML (no-mans-land) support.
25 **              Improved performance (especially for free()ing!).
26 **              Support for 'read-only' buffers (checksums)
27 **              ^^ NOTE: I never did this... maybe I should?
28 **              FBI (free'd block info) tagged before freed blocks
29 **              Exporting of the mwCounter variable
30 **              mwBreakOut() localizes debugger support
31 **              Allocation statistics (global, per-module, per-line)
32 **              Self-repair ability with relinking
33 ** 950913 JLI   [2.10 improved garbage handling]
34 ** 951201 JLI   [2.11 improved auto-free in emergencies]
35 ** 960125 JLI   [X.01 implemented auto-checking using mwAutoCheck()]
36 ** 960514 JLI   [2.12 undefining of existing macros]
37 ** 960515 JLI   [2.13 possibility to use default new() & delete()]
38 ** 960516 JLI   [2.20 suppression of file flushing on unfreed msgs]
39 ** 960516 JLI   [2.21 better support for using MEMWATCH with DLL's]
40 ** 960710 JLI   [X.02 multiple logs and mwFlushNow()]
41 ** 960801 JLI   [2.22 merged X.01 version with current]
42 ** 960805 JLI   [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
43 ** 960805 JLI   [2.31 merged X.02 version with current]
44 ** 961002 JLI   [2.32 support for realloc() + fixed STDERR bug]
45 ** 961222 JLI   [2.40 added mwMark() & mwUnmark()]
46 ** 970101 JLI   [2.41 added over/underflow checking after failed ASSERT/VERIFY]
47 ** 970113 JLI   [2.42 added support for PC-Lint 7.00g]
48 ** 970207 JLI   [2.43 added support for strdup()]
49 ** 970209 JLI   [2.44 changed default filename to lowercase]
50 ** 970405 JLI   [2.45 fixed bug related with atexit() and some C++ compilers]
51 ** 970723 JLI   [2.46 added MW_ARI_NULLREAD flag]
52 ** 970813 JLI   [2.47 stabilized marker handling]
53 ** 980317 JLI   [2.48 ripped out C++ support; wasn't working good anyway]
54 ** 980318 JLI   [2.50 improved self-repair facilities & SIGSEGV support]
55 ** 980417 JLI	[2.51 more checks for invalid addresses]
56 ** 980512 JLI	[2.52 moved MW_ARI_NULLREAD to occur before aborting]
57 ** 990112 JLI	[2.53 added check for empty heap to mwIsOwned]
58 ** 990217 JLI	[2.55 improved the emergency repairs diagnostics and NML]
59 ** 990224 JLI	[2.56 changed ordering of members in structures]
60 ** 990303 JLI	[2.57 first maybe-fixit-for-hpux test]
61 ** 990516 JLI	[2.58 added 'static' to the definition of mwAutoInit]
62 ** 990517 JLI	[2.59 fixed some high-sensitivity warnings]
63 ** 990610 JLI	[2.60 fixed some more high-sensitivity warnings]
64 ** 990715 JLI	[2.61 changed TRACE/ASSERT/VERIFY macro names]
65 ** 991001 JLI	[2.62 added CHECK_BUFFER() and mwTestBuffer()]
66 ** 991007 JLI	[2.63 first shot at a 64-bit compatible version]
67 ** 991009 JLI	[2.64 undef's strdup() if defined, mwStrdup made const]
68 */
69 
70 #define __MEMWATCH_C 1
71 
72 #ifdef MW_NOCPP
73 #define MEMWATCH_NOCPP
74 #endif
75 #ifdef MW_STDIO
76 #define MEMWATCH_STDIO
77 #endif
78 
79 /***********************************************************************
80 ** Include files
81 ***********************************************************************/
82 
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <stdarg.h>
86 #include <string.h>
87 #include <signal.h>
88 #include <setjmp.h>
89 #include <time.h>
90 #include <limits.h>
91 #include "memwatch.h"
92 
93 #ifndef toupper
94 #include <ctype.h>
95 #endif
96 
97 /***********************************************************************
98 ** Defines & other weird stuff
99 ***********************************************************************/
100 
101 /*lint -save -e767 */
102 #define VERSION     "2.64"         /* the current version number */
103 #define CHKVAL(mw)  (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line)
104 #define FLUSH()     mwFlush()
105 #define TESTS(f,l)  if(mwTestAlways) (void)mwTestNow(f,l,1)
106 #define PRECHK      0x01234567L
107 #define POSTCHK     0x76543210L
108 #define mwBUFFER_TO_MW(p) ( (mwData*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) )
109 /*lint -restore */
110 
111 #define MW_NML      0x0001
112 
113 #ifdef _MSC_VER
114 #define COMMIT "c"  /* Microsoft C requires the 'c' to perform as desired */
115 #else
116 #define COMMIT ""   /* Normal ANSI */
117 #endif /* _MSC_VER */
118 
119 #ifdef __cplusplus
120 #define CPPTEXT "++"
121 #else
122 #define CPPTEXT ""
123 #endif /* __cplusplus */
124 
125 #ifdef MEMWATCH_STDIO
126 #define mwSTDERR stderr
127 #else
128 #define mwSTDERR mwLog
129 #endif
130 
131 /***********************************************************************
132 ** If you really, really know what you're doing,
133 ** you can predefine these things yourself.
134 ***********************************************************************/
135 
136 #ifndef mwBYTE_DEFINED
137 #if CHAR_BIT != 8
138 #error need CHAR_BIT to be 8!
139 #else
140 typedef unsigned char mwBYTE;
141 #define mwBYTE_DEFINED 1
142 #endif
143 #endif
144 
145 #if UINT_MAX <= 0xFFFFUL
146 # define mw16BIT 1
147 # define mwROUNDALLOC_DEFAULT	2
148 #else
149 # if ULONG_MAX > 0xFFFFFFFFUL
150 #  define mw64BIT 1
151 # define mwROUNDALLOC_DEFAULT	8
152 # else
153 #  define mw32BIT 1
154 # define mwROUNDALLOC_DEFAULT	4
155 # endif
156 #endif
157 
158 /* mwROUNDALLOC is the number of bytes to */
159 /* round up to, to ensure that the end of */
160 /* the buffer is suitable for storage of */
161 /* any kind of object */
162 #ifndef mwROUNDALLOC
163 # define mwROUNDALLOC mwROUNDALLOC_DEFAULT
164 #endif
165 
166 #ifndef mwDWORD_DEFINED
167 #if ULONG_MAX == 0xFFFFFFFFUL
168 typedef unsigned long mwDWORD;
169 #define mwDWORD_DEFINED "unsigned long"
170 #endif
171 #endif
172 
173 #ifndef mwDWORD_DEFINED
174 #if UINT_MAX == 0xFFFFFFFFUL
175 typedef unsigned int mwDWORD;
176 #define mwDWORD_DEFINED "unsigned int"
177 #endif
178 #endif
179 
180 #ifndef mwDWORD_DEFINED
181 #if USHRT_MAX == 0xFFFFFFFFUL
182 typedef unsigned short mwDWORD;
183 #define mwDWORD_DEFINED "unsigned short"
184 #endif
185 #endif
186 
187 #ifndef mwBYTE_DEFINED
188 #error "can't find out the correct type for a 8 bit scalar"
189 #endif
190 
191 #ifndef mwDWORD_DEFINED
192 #error "can't find out the correct type for a 32 bit scalar"
193 #endif
194 
195 /***********************************************************************
196 ** Typedefs & structures
197 ***********************************************************************/
198 
199 /* main data holding area, precedes actual allocation */
200 typedef struct mwData_ mwData;
201 struct mwData_ {
202     mwData*     prev;   /* previous allocation in chain */
203     mwData*     next;   /* next allocation in chain */
204     const char* file;   /* file name where allocated */
205     long        count;  /* action count */
206     long        check;  /* integrity check value */
207 #if 0
208     long        crc;    /* data crc value */
209 #endif
210     size_t      size;   /* size of allocation */
211     int         line;   /* line number where allocated */
212     unsigned    flag;   /* flag word */
213     };
214 
215 /* statistics structure */
216 typedef struct mwStat_ mwStat;
217 struct mwStat_ {
218     mwStat*     next;   /* next statistic buffer */
219     const char* file;
220     long        total;  /* total bytes allocated */
221     long        num;    /* total number of allocations */
222     long        max;    /* max allocated at one time */
223     long        curr;   /* current allocations */
224     int         line;
225     };
226 
227 /* grabbing structure, 1K in size */
228 typedef struct mwGrabData_ mwGrabData;
229 struct mwGrabData_ {
230     mwGrabData* next;
231     int         type;
232     char        blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ];
233     };
234 
235 typedef struct mwMarker_ mwMarker;
236 struct mwMarker_ {
237     void *host;
238     char *text;
239     mwMarker *next;
240     int level;
241     };
242 
243 /***********************************************************************
244 ** Static variables
245 ***********************************************************************/
246 
247 static int      mwInited =      0;
248 static int      mwInfoWritten = 0;
249 static int      mwUseAtexit =   0;
250 static FILE*    mwLog =         NULL;
251 static int      mwFlushing =    0;
252 static int      mwStatLevel =   MW_STAT_DEFAULT;
253 static int      mwNML =         MW_NML_DEFAULT;
254 static int      mwFBI =         0;
255 static long     mwAllocLimit =  0L;
256 static int      mwUseLimit =    0;
257 
258 static long     mwNumCurAlloc = 0L;
259 static mwData*  mwHead = 		NULL;
260 static mwData*  mwTail = 		NULL;
261 static int		mwDataSize =	0;
262 static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch";
263 static int		mwOverflowZoneSize = mwROUNDALLOC;
264 
265 static void     (*mwOutFunction)(int) = NULL;
266 static int      (*mwAriFunction)(const char*) = NULL;
267 static int      mwAriAction = MW_ARI_ABORT;
268 
269 static char     mwPrintBuf[MW_TRACE_BUFFER+8];
270 
271 static unsigned long mwCounter = 0L;
272 static long     mwErrors =      0L;
273 
274 static int      mwTestFlags =   0;
275 static int      mwTestAlways =  0;
276 
277 static FILE*    mwLogB1 =       NULL;
278 static int      mwFlushingB1 =  0;
279 
280 static mwStat*  mwStatList = NULL;
281 static long     mwStatTotAlloc = 0L;
282 static long     mwStatMaxAlloc = 0L;
283 static long     mwStatNumAlloc = 0L;
284 static long     mwStatCurAlloc = 0L;
285 static long     mwNmlNumAlloc = 0L;
286 static long     mwNmlCurAlloc = 0L;
287 
288 static mwGrabData* mwGrabList = NULL;
289 static long     mwGrabSize = 0L;
290 
291 static void *   mwLastFree[MW_FREE_LIST];
292 static const char *mwLFfile[MW_FREE_LIST];
293 static int      mwLFline[MW_FREE_LIST];
294 static int      mwLFcur = 0;
295 
296 static mwMarker* mwFirstMark = NULL;
297 
298 static FILE*    mwLogB2 =       NULL;
299 static int      mwFlushingB2 =  0;
300 
301 /***********************************************************************
302 ** Static function declarations
303 ***********************************************************************/
304 
305 static void     mwAutoInit( void );
306 static FILE*    mwLogR( void );
307 static void     mwLogW( FILE* );
308 static int      mwFlushR( void );
309 static void     mwFlushW( int );
310 static void     mwFlush( void );
311 static void     mwIncErr( void );
312 static void     mwUnlink( mwData*, const char* file, int line );
313 static int      mwRelink( mwData*, const char* file, int line );
314 static int      mwIsHeapOK( mwData *mw );
315 static int      mwIsOwned( mwData* mw, const char* file, int line );
316 static int      mwTestBuf( mwData* mw, const char* file, int line );
317 static void     mwDefaultOutFunc( int );
318 static void     mwWrite( const char* format, ... );
319 static void     mwLogFile( const char* name );
320 static size_t   mwFreeUp( size_t, int );
321 static const void *mwTestMem( const void *, unsigned, int );
322 static int      mwStrCmpI( const char *s1, const char *s2 );
323 static int      mwTestNow( const char *file, int line, int always_invoked );
324 static void     mwDropAll( void );
325 static const char *mwGrabType( int type );
326 static unsigned mwGrab_( unsigned kb, int type, int silent );
327 static unsigned mwDrop_( unsigned kb, int type, int silent );
328 static int      mwARI( const char* text );
329 static void     mwStatReport( void );
330 static mwStat*  mwStatGet( const char*, int, int );
331 static void     mwStatAlloc( size_t, const char*, int );
332 static void     mwStatFree( size_t, const char*, int );
333 static int		mwCheckOF( const void * p );
334 static void		mwWriteOF( void * p );
335 static char		mwDummy( char c );
336 
337 /***********************************************************************
338 ** System functions
339 ***********************************************************************/
340 
mwInit(void)341 void mwInit( void ) {
342     time_t tid;
343 
344     if( mwInited++ > 0 ) return;
345 
346     /* start a log if none is running */
347     if( mwLogR() == NULL ) mwLogFile( "memwatch.log" );
348     if( mwLogR() == NULL ) {
349         int i;
350         char buf[32];
351         /* oops, could not open it! */
352         /* probably because it's already open */
353         /* so we try some other names */
354         for( i=1; i<100; i++ ) {
355             sprintf( buf, "memwat%02d.log", i );
356             mwLogFile( buf );
357             if( mwLogR() != NULL ) break;
358             }
359         }
360 
361     /* initialize the statistics */
362     mwStatList = NULL;
363     mwStatTotAlloc = 0L;
364     mwStatCurAlloc = 0L;
365     mwStatMaxAlloc = 0L;
366     mwStatNumAlloc = 0L;
367 	mwNmlCurAlloc = 0L;
368 	mwNmlNumAlloc = 0L;
369 
370 	/* calculate the buffer size to use for a mwData */
371 	mwDataSize = sizeof(mwData);
372 	while( mwDataSize % mwROUNDALLOC ) mwDataSize ++;
373 
374     /* write informational header if needed */
375     if( !mwInfoWritten ) {
376         mwInfoWritten = 1;
377         (void) time( &tid );
378         mwWrite(
379             "\n============="
380             " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh "
381             "=============\n");
382         mwWrite( "\nStarted at %s\n", ctime( &tid ) );
383 
384 /**************************************************************** Generic */
385 		mwWrite( "Modes: " );
386 #ifdef mwNew
387         mwWrite( "C++ " );
388 #endif /* mwNew */
389 #ifdef __STDC__
390         mwWrite( "__STDC__ " );
391 #endif /* __STDC__ */
392 #ifdef mw16BIT
393 		mwWrite( "16-bit " );
394 #endif
395 #ifdef mw32BIT
396 		mwWrite( "32-bit " );
397 #endif
398 #ifdef mw64BIT
399 		mwWrite( "64-bit " );
400 #endif
401 		mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" );
402 		mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n",
403 			mwROUNDALLOC, sizeof(mwData), mwDataSize );
404 /**************************************************************** Generic */
405 
406 /************************************************************ Microsoft C */
407 #ifdef _MSC_VER
408         mwWrite( "Compiled using Microsoft C" CPPTEXT
409             " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 );
410 #endif /* _MSC_VER */
411 /************************************************************ Microsoft C */
412 
413 /************************************************************** Borland C */
414 #ifdef __BORLANDC__
415         mwWrite( "Compiled using Borland C"
416 #ifdef __cplusplus
417             "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 );
418 #else
419             " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 );
420 #endif /* __cplusplus */
421 #endif /* __BORLANDC__ */
422 /************************************************************** Borland C */
423 
424 /************************************************************** Watcom C */
425 #ifdef __WATCOMC__
426         mwWrite( "Compiled using Watcom C %d.%02d ",
427             __WATCOMC__/100, __WATCOMC__%100 );
428 #ifdef __FLAT__
429         mwWrite( "(32-bit flat model)" );
430 #endif /* __FLAT__ */
431         mwWrite( "\n" );
432 #endif /* __WATCOMC__ */
433 /************************************************************** Watcom C */
434 
435         mwWrite( "\n" );
436         FLUSH();
437         }
438 
439     if( mwUseAtexit ) (void) atexit( mwAbort );
440     return;
441     }
442 
mwAbort(void)443 void mwAbort( void ) {
444     mwData *mw;
445     mwMarker *mrk;
446     char *data;
447     time_t tid;
448     int c, i, j;
449 	int errors;
450 
451     tid = time( NULL );
452     mwWrite( "\nStopped at %s\n", ctime( &tid) );
453 
454     if( !mwInited )
455         mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" );
456 
457     /* release the grab list */
458     mwDropAll();
459 
460     /* report mwMarked items */
461     while( mwFirstMark ) {
462         mrk = mwFirstMark->next;
463         mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text );
464         free( mwFirstMark->text );
465         free( mwFirstMark );
466         mwFirstMark = mrk;
467         mwErrors ++;
468         }
469 
470     /* release all still allocated memory */
471 	errors = 0;
472     while( mwHead != NULL && errors < 3 ) {
473 		if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) {
474 			if( errors < 3 )
475 			{
476 				errors ++;
477 				mwWrite( "internal: NML/unfreed scan restarting\n" );
478 				FLUSH();
479 				mwHead = mwHead;
480 				continue;
481 			}
482 			mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" );
483 			FLUSH();
484 			break;
485 			}
486         mwFlushW(0);
487         if( !(mwHead->flag & MW_NML) ) {
488             mwErrors++;
489             data = ((char*)mwHead)+mwDataSize;
490             mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ",
491                 mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize );
492             if( mwCheckOF( data ) ) {
493                 mwWrite( "[underflowed] ");
494                 FLUSH();
495                 }
496             if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) {
497                 mwWrite( "[overflowed] ");
498                 FLUSH();
499                 }
500             mwWrite( " \t{" );
501             j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size;
502             for( i=0;i<16;i++ ) {
503                 if( i<j ) mwWrite( "%02X ",
504                     (unsigned char) *(data+mwOverflowZoneSize+i) );
505                 else mwWrite( ".. " );
506                 }
507             for( i=0;i<j;i++ ) {
508                 c = *(data+mwOverflowZoneSize+i);
509                 if( c < 32 || c > 126 ) c = '.';
510                 mwWrite( "%c", c );
511                 }
512             mwWrite( "}\n" );
513 			mw = mwHead;
514 			mwUnlink( mw, __FILE__, __LINE__ );
515             free( mw );
516             }
517         else {
518             data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize;
519             if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) {
520                 mwErrors++;
521                 mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
522                     mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line );
523                 FLUSH();
524                 }
525 			mwNmlNumAlloc --;
526 			mwNmlCurAlloc -= mwHead->size;
527 			mw = mwHead;
528 			mwUnlink( mw, __FILE__, __LINE__ );
529             free( mw );
530             }
531         }
532 
533 	if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc );
534 	if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc );
535 
536     /* report statistics */
537     mwStatReport();
538     FLUSH();
539 
540     mwInited = 0;
541     mwHead = mwTail = NULL;
542     if( mwErrors )
543         fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors);
544     mwLogFile( NULL );
545     mwErrors = 0;
546     }
547 
mwTerm(void)548 void mwTerm( void ) {
549     if( mwInited == 1 )
550     {
551         mwAbort();
552         return;
553     }
554     if( !mwInited )
555         mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n");
556     else
557         mwInited --;
558     }
559 
mwStatistics(int level)560 void mwStatistics( int level )
561 {
562     mwAutoInit();
563     if( level<0 ) level=0;
564     if( mwStatLevel != level )
565     {
566 		mwWrite( "statistics: now collecting on a %s basis\n",
567 			level<1?"global":(level<2?"module":"line") );
568 	    mwStatLevel = level;
569 	}
570 }
571 
mwAutoCheck(int onoff)572 void mwAutoCheck( int onoff ) {
573     mwAutoInit();
574     mwTestAlways = onoff;
575     if( onoff ) mwTestFlags = MW_TEST_ALL;
576     }
577 
mwSetOutFunc(void (* func)(int))578 void mwSetOutFunc( void (*func)(int) ) {
579     mwAutoInit();
580     mwOutFunction = func;
581     }
582 
mwWriteOF(void * p)583 void mwWriteOF( void *p )
584 {
585 	int i;
586 	unsigned char *ptr;
587 	ptr = (unsigned char*) p;
588 	for( i=0; i<mwOverflowZoneSize; i++ )
589 	{
590 		*(ptr+i) = mwOverflowZoneTemplate[i%8];
591 	}
592 	return;
593 }
594 
mwCheckOF(const void * p)595 int mwCheckOF( const void *p )
596 {
597 	int i;
598 	const unsigned char *ptr;
599 	ptr = (const unsigned char *) p;
600 	for( i=0; i<mwOverflowZoneSize; i++ )
601 	{
602 		if( *(ptr+i) != mwOverflowZoneTemplate[i%8] )
603 			return 1; /* errors found */
604 	}
605 	return 0; /* no errors */
606 }
607 
mwTest(const char * file,int line,int items)608 int mwTest( const char *file, int line, int items ) {
609     mwAutoInit();
610     mwTestFlags = items;
611     return mwTestNow( file, line, 0 );
612     }
613 
614 /*
615 ** Returns zero if there are no errors.
616 ** Returns nonzero if there are errors.
617 */
mwTestBuffer(const char * file,int line,void * p)618 int mwTestBuffer( const char *file, int line, void *p ) {
619     mwData* mw;
620 
621     mwAutoInit();
622 
623     /* do the quick ownership test */
624     mw = (mwData*) mwBUFFER_TO_MW( p );
625 
626     if( mwIsOwned( mw, file, line ) ) {
627         return mwTestBuf( mw, file, line );
628 		}
629 	return 1;
630 	}
631 
mwBreakOut(const char * cause)632 void mwBreakOut( const char* cause ) {
633     fprintf(mwSTDERR, "breakout: %s\n", cause);
634     mwWrite("breakout: %s\n", cause );
635     return;
636     }
637 
638 /*
639 ** 981217 JLI: is it possible that ->next is not always set?
640 */
mwMark(void * p,const char * desc,const char * file,unsigned line)641 void * mwMark( void *p, const char *desc, const char *file, unsigned line ) {
642     mwMarker *mrk;
643     unsigned n, isnew;
644     char *buf;
645     int tot, oflow = 0;
646     char wherebuf[128];
647 
648     mwAutoInit();
649     TESTS(NULL,0);
650 
651     if( desc == NULL ) desc = "unknown";
652     if( file == NULL ) file = "unknown";
653 
654     tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line );
655     if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; }
656 
657     if( p == NULL ) {
658         mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc );
659         return p;
660         }
661 
662 	if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) )
663 	{
664 		mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
665 			file, line, mwFirstMark, desc );
666 		return p;
667 	}
668 
669     for( mrk=mwFirstMark; mrk; mrk=mrk->next )
670 	{
671 		if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) )
672 		{
673 			mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
674 				file, line, mrk, mrk->next, desc );
675 			return p;
676 		}
677 		if( mrk->host == p ) break;
678 	}
679 
680     if( mrk == NULL ) {
681         isnew = 1;
682         mrk = (mwMarker*) malloc( sizeof( mwMarker ) );
683         if( mrk == NULL ) {
684             mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
685             return p;
686             }
687 		mrk->next = NULL;
688         n = 0;
689         }
690     else {
691         isnew = 0;
692         n = strlen( mrk->text );
693         }
694 
695     n += strlen( wherebuf );
696     buf = (char*) malloc( n+3 );
697     if( buf == NULL ) {
698         if( isnew ) free( mrk );
699         mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
700         return p;
701         }
702 
703     if( isnew ) {
704         memcpy( buf, wherebuf, n+1 );
705         mrk->next = mwFirstMark;
706         mrk->host = p;
707         mrk->text = buf;
708         mrk->level = 1;
709         mwFirstMark = mrk;
710         }
711     else {
712         strcpy( buf, mrk->text );
713         strcat( buf, ", " );
714         strcat( buf, wherebuf );
715         free( mrk->text );
716         mrk->text = buf;
717         mrk->level ++;
718         }
719 
720     if( oflow ) {
721         mwIncErr();
722         mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
723         }
724     return p;
725     }
726 
mwUnmark(void * p,const char * file,unsigned line)727 void* mwUnmark( void *p, const char *file, unsigned line ) {
728     mwMarker *mrk, *prv;
729     mrk = mwFirstMark;
730     prv = NULL;
731     while( mrk ) {
732         if( mrk->host == p ) {
733             if( mrk->level < 2 ) {
734                 if( prv ) prv->next = mrk->next;
735                 else mwFirstMark = mrk->next;
736                 free( mrk->text );
737                 free( mrk );
738                 return p;
739                 }
740             mrk->level --;
741             return p;
742             }
743         prv = mrk;
744         mrk = mrk->next;
745         }
746     mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p );
747     return p;
748     }
749 
750 
751 /***********************************************************************
752 ** Abort/Retry/Ignore handlers
753 ***********************************************************************/
754 
mwARI(const char * estr)755 static int mwARI( const char *estr ) {
756     char inbuf[81];
757     int c;
758     fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
759     (void) fgets(inbuf,sizeof(inbuf),stdin);
760 	for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ;
761     c = inbuf[c];
762     if( c == 'R' || c == 'r' ) {
763         mwBreakOut( estr );
764         return MW_ARI_RETRY;
765         }
766     if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE;
767     return MW_ARI_ABORT;
768     }
769 
770 /* standard ARI handler (exported) */
mwAriHandler(const char * estr)771 int mwAriHandler( const char *estr ) {
772     mwAutoInit();
773     return mwARI( estr );
774     }
775 
776 /* used to set the ARI function */
mwSetAriFunc(int (* func)(const char *))777 void mwSetAriFunc( int (*func)(const char *) ) {
778     mwAutoInit();
779     mwAriFunction = func;
780     }
781 
782 /***********************************************************************
783 ** Allocation handlers
784 ***********************************************************************/
785 
mwMalloc(size_t size,const char * file,int line)786 void* mwMalloc( size_t size, const char* file, int line) {
787     size_t needed;
788     mwData *mw;
789     char *ptr;
790     void *p;
791 
792     mwAutoInit();
793 
794     TESTS(file,line);
795 
796     mwCounter ++;
797     needed = mwDataSize + mwOverflowZoneSize*2 + size;
798 
799     /* if this allocation would violate the limit, fail it */
800     if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) {
801         mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
802             mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc );
803         mwIncErr();
804         FLUSH();
805         return NULL;
806         }
807 
808     mw = (mwData*) malloc( needed );
809     if( mw == NULL ) {
810         if( mwFreeUp(needed,0) >= needed ) {
811             mw = (mwData*) malloc(needed);
812             if( mw == NULL ) {
813                 mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed );
814                 mwIncErr();
815                 FLUSH();
816                 }
817             }
818         if( mw == NULL ) {
819             mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
820                 mwCounter, file, line, (long)size, mwStatCurAlloc );
821             mwIncErr();
822             FLUSH();
823             return NULL;
824             }
825         }
826 
827     mw->count = mwCounter;
828     mw->prev = NULL;
829     mw->next = mwHead;
830     mw->file = file;
831     mw->size = size;
832     mw->line = line;
833     mw->flag = 0;
834     mw->check = CHKVAL(mw);
835 
836     if( mwHead ) mwHead->prev = mw;
837     mwHead = mw;
838     if( mwTail == NULL ) mwTail = mw;
839 
840     ptr = ((char*)mw) + mwDataSize;
841 	mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */
842     ptr += mwOverflowZoneSize;
843     p = ptr;
844     memset( ptr, MW_VAL_NEW, size );
845     ptr += size;
846     mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */
847 
848     mwNumCurAlloc ++;
849     mwStatCurAlloc += (long) size;
850     mwStatTotAlloc += (long) size;
851     if( mwStatCurAlloc > mwStatMaxAlloc )
852         mwStatMaxAlloc = mwStatCurAlloc;
853     mwStatNumAlloc ++;
854 
855     if( mwStatLevel ) mwStatAlloc( size, file, line );
856 
857     return p;
858     }
859 
mwRealloc(void * p,size_t size,const char * file,int line)860 void* mwRealloc( void *p, size_t size, const char* file, int line) {
861     int oldUseLimit, i;
862     mwData *mw;
863     char *ptr;
864 
865     mwAutoInit();
866 
867     if( p == NULL ) return mwMalloc( size, file, line );
868     if( size == 0 ) { mwFree( p, file, line ); return NULL; }
869 
870     /* do the quick ownership test */
871     mw = (mwData*) mwBUFFER_TO_MW( p );
872     if( mwIsOwned( mw, file, line ) ) {
873 
874 		/* if the buffer is an NML, treat this as a double-free */
875 		if( mw->flag & MW_NML )
876 		{
877             mwIncErr();
878 			if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
879 			{
880 				mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
881 					mwCounter, file, line, mw );
882 			}
883 			goto check_dbl_free;
884 		}
885 
886         /* if this allocation would violate the limit, fail it */
887         if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) {
888             TESTS(file,line);
889             mwCounter ++;
890             mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
891                 mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc );
892             mwIncErr();
893             FLUSH();
894             return NULL;
895             }
896 
897         /* fake realloc operation */
898         oldUseLimit = mwUseLimit;
899         mwUseLimit = 0;
900         ptr = (char*) mwMalloc( size, file, line );
901         if( ptr != NULL ) {
902             if( size < mw->size )
903                 memcpy( ptr, p, size );
904             else
905                 memcpy( ptr, p, mw->size );
906             mwFree( p, file, line );
907             }
908         mwUseLimit = oldUseLimit;
909         return (void*) ptr;
910         }
911 
912     /* Unknown pointer! */
913 
914     /* using free'd pointer? */
915 check_dbl_free:
916     for(i=0;i<MW_FREE_LIST;i++) {
917         if( mwLastFree[i] == p ) {
918             mwIncErr();
919             mwWrite( "realloc: <%ld> %s(%d), %p was"
920                 " freed from %s(%d)\n",
921                 mwCounter, file, line, p,
922                 mwLFfile[i], mwLFline[i] );
923             FLUSH();
924             return NULL;
925             }
926         }
927 
928     /* some weird pointer */
929     mwIncErr();
930     mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n",
931         mwCounter, file, line, p );
932     FLUSH();
933     return NULL;
934     }
935 
mwStrdup(const char * str,const char * file,int line)936 char *mwStrdup( const char* str, const char* file, int line ) {
937     size_t len;
938     char *newstring;
939     if( str == NULL ) {
940         mwIncErr();
941         mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n",
942             mwCounter, file, line );
943         FLUSH();
944         return NULL;
945         }
946     len = strlen( str ) + 1;
947     newstring = (char*) mwMalloc( len, file, line );
948     if( newstring != NULL ) memcpy( newstring, str, len );
949     return newstring;
950     }
951 
mwFree(void * p,const char * file,int line)952 void mwFree( void* p, const char* file, int line ) {
953     int i;
954     mwData* mw;
955     char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ];
956 
957     TESTS(file,line);
958 
959     /* this code is in support of C++ delete */
960     if( file == NULL ) {
961         mwFree_( p );
962         return;
963         }
964 
965     mwAutoInit();
966     mwCounter ++;
967 
968     /* on NULL free, write a warning and return */
969     if( p == NULL ) {
970         mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n",
971             mwCounter, file, line );
972         FLUSH();
973         return;
974         }
975 
976     /* do the quick ownership test */
977     mw = (mwData*) mwBUFFER_TO_MW( p );
978 
979     if( mwIsOwned( mw, file, line ) ) {
980         (void) mwTestBuf( mw, file, line );
981 
982 		/* if the buffer is an NML, treat this as a double-free */
983 		if( mw->flag & MW_NML )
984 		{
985 			if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
986 			{
987 				mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
988 					mwCounter, file, line, mw );
989 			}
990 			goto check_dbl_free;
991 		}
992 
993         /* update the statistics */
994         mwNumCurAlloc --;
995         mwStatCurAlloc -= (long) mw->size;
996         if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line );
997 
998         /* we should either free the allocation or keep it as NML */
999         if( mwNML ) {
1000             mw->flag |= MW_NML;
1001 			mwNmlNumAlloc ++;
1002 			mwNmlCurAlloc += (long) mw->size;
1003             memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size );
1004             }
1005         else {
1006             /* unlink the allocation, and enter the post-free data */
1007             mwUnlink( mw, file, line );
1008             memset( mw, MW_VAL_DEL,
1009                 mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize );
1010             if( mwFBI ) {
1011                 memset( mw, '.', mwDataSize + mwOverflowZoneSize );
1012                 sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line );
1013                 strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize );
1014                 }
1015             free( mw );
1016             }
1017 
1018         /* add the pointer to the last-free track */
1019         mwLFfile[ mwLFcur ] = file;
1020         mwLFline[ mwLFcur ] = line;
1021         mwLastFree[ mwLFcur++ ] = p;
1022         if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0;
1023 
1024         return;
1025         }
1026 
1027     /* check for double-freeing */
1028 check_dbl_free:
1029     for(i=0;i<MW_FREE_LIST;i++) {
1030         if( mwLastFree[i] == p ) {
1031             mwIncErr();
1032             mwWrite( "double-free: <%ld> %s(%d), %p was"
1033                 " freed from %s(%d)\n",
1034                 mwCounter, file, line, p,
1035                 mwLFfile[i], mwLFline[i] );
1036             FLUSH();
1037             return;
1038             }
1039         }
1040 
1041     /* some weird pointer... block the free */
1042     mwIncErr();
1043     mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n",
1044         mwCounter, file, line, p );
1045     FLUSH();
1046     return;
1047     }
1048 
mwCalloc(size_t a,size_t b,const char * file,int line)1049 void* mwCalloc( size_t a, size_t b, const char *file, int line ) {
1050     void *p;
1051     size_t size = a * b;
1052     p = mwMalloc( size, file, line );
1053     if( p == NULL ) return NULL;
1054     memset( p, 0, size );
1055     return p;
1056     }
1057 
mwFree_(void * p)1058 void mwFree_( void *p ) {
1059     TESTS(NULL,0);
1060     free(p);
1061     }
1062 
mwMalloc_(size_t size)1063 void* mwMalloc_( size_t size ) {
1064     TESTS(NULL,0);
1065     return malloc( size );
1066     }
1067 
mwRealloc_(void * p,size_t size)1068 void* mwRealloc_( void *p, size_t size ) {
1069     TESTS(NULL,0);
1070     return realloc( p, size );
1071     }
1072 
mwCalloc_(size_t a,size_t b)1073 void* mwCalloc_( size_t a, size_t b ) {
1074     TESTS(NULL,0);
1075     return calloc( a, b );
1076     }
1077 
mwFlushNow(void)1078 void mwFlushNow( void ) {
1079     if( mwLogR() ) fflush( mwLogR() );
1080     return;
1081     }
1082 
mwDoFlush(int onoff)1083 void mwDoFlush( int onoff ) {
1084     mwFlushW( onoff<1?0:onoff );
1085     if( onoff ) if( mwLogR() ) fflush( mwLogR() );
1086     return;
1087     }
1088 
mwLimit(long lim)1089 void mwLimit( long lim ) {
1090     TESTS(NULL,0);
1091     mwWrite("limit: old limit = ");
1092     if( !mwAllocLimit ) mwWrite( "none" );
1093     else mwWrite( "%ld bytes", mwAllocLimit );
1094     mwWrite( ", new limit = ");
1095     if( !lim ) {
1096         mwWrite( "none\n" );
1097         mwUseLimit = 0;
1098         }
1099     else {
1100         mwWrite( "%ld bytes\n", lim );
1101         mwUseLimit = 1;
1102         }
1103     mwAllocLimit = lim;
1104     FLUSH();
1105     }
1106 
mwSetAriAction(int action)1107 void mwSetAriAction( int action ) {
1108     TESTS(NULL,0);
1109     mwAriAction = action;
1110     return;
1111     }
1112 
mwAssert(int exp,const char * exps,const char * fn,int ln)1113 int mwAssert( int exp, const char *exps, const char *fn, int ln ) {
1114     int i;
1115     char buffer[MW_TRACE_BUFFER+8];
1116     TESTS(fn,ln);
1117     if( exp ) return 0;
1118     mwAutoInit();
1119     mwIncErr();
1120     mwCounter++;
1121     mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
1122     if( mwAriFunction != NULL ) {
1123         sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps );
1124         i = (*mwAriFunction)(buffer);
1125 		switch( i ) {
1126 			case MW_ARI_IGNORE:
1127 	           	mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter );
1128     	        return 0;
1129 			case MW_ARI_RETRY:
1130             	mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter );
1131             	return 1;
1132 			}
1133         }
1134     else {
1135         if( mwAriAction & MW_ARI_IGNORE ) {
1136             mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
1137             return 0;
1138             }
1139         fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps );
1140         }
1141 
1142     FLUSH();
1143     (void) mwTestNow( fn, ln, 1 );
1144     FLUSH();
1145 
1146 	if( mwAriAction & MW_ARI_NULLREAD ) {
1147 		/* This is made in an attempt to kick in */
1148 		/* any debuggers or OS stack traces */
1149 	    FLUSH();
1150 		/*lint -save -e413 */
1151 		i = *((int*)NULL);
1152 		mwDummy( (char)i );
1153 		/*lint -restore */
1154 		}
1155 
1156     exit(255);
1157     /* NOT REACHED - the return statement is in to keep */
1158     /* stupid compilers from squeaking about differing return modes. */
1159     /* Smart compilers instead say 'code unreachable...' */
1160     /*lint -save -e527 */
1161     return 0;
1162     /*lint -restore */
1163     }
1164 
mwVerify(int exp,const char * exps,const char * fn,int ln)1165 int mwVerify( int exp, const char *exps, const char *fn, int ln ) {
1166     int i;
1167     char buffer[MW_TRACE_BUFFER+8];
1168     TESTS(fn,ln);
1169     if( exp ) return 0;
1170     mwAutoInit();
1171     mwIncErr();
1172     mwCounter++;
1173     mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
1174     if( mwAriFunction != NULL ) {
1175         sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps );
1176         i = (*mwAriFunction)(buffer);
1177         if( i == 0 ) {
1178             mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter );
1179             return 0;
1180             }
1181         if( i == 1 ) {
1182             mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter );
1183             return 1;
1184             }
1185         }
1186     else {
1187         if( mwAriAction & MW_ARI_NULLREAD ) {
1188             /* This is made in an attempt to kick in */
1189             /* any debuggers or OS stack traces */
1190 		    FLUSH();
1191             /*lint -save -e413 */
1192             i = *((int*)NULL);
1193 			mwDummy( (char)i );
1194             /*lint -restore */
1195             }
1196         if( mwAriAction & MW_ARI_IGNORE ) {
1197             mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
1198             return 0;
1199             }
1200         fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps );
1201         }
1202     FLUSH();
1203     (void) mwTestNow( fn, ln, 1 );
1204     FLUSH();
1205 	exit(255);
1206     /* NOT REACHED - the return statement is in to keep */
1207     /* stupid compilers from squeaking about differing return modes. */
1208     /* Smart compilers instead say 'code unreachable...' */
1209     /*lint -save -e527 */
1210     return 0;
1211     /*lint -restore */
1212     }
1213 
mwTrace(const char * format,...)1214 void mwTrace( const char *format, ... ) {
1215     int tot, oflow = 0;
1216     va_list mark;
1217 
1218     mwAutoInit();
1219     TESTS(NULL,0);
1220     if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
1221 
1222     va_start( mark, format );
1223     tot = vsprintf( mwPrintBuf, format, mark );
1224     va_end( mark );
1225     if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
1226     for(tot=0;mwPrintBuf[tot];tot++)
1227         (*mwOutFunction)( mwPrintBuf[tot] );
1228     if( oflow ) {
1229         mwIncErr();
1230         mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
1231         }
1232 
1233     FLUSH();
1234     }
1235 
1236 
1237 /***********************************************************************
1238 ** Grab & Drop
1239 ***********************************************************************/
1240 
mwGrab(unsigned kb)1241 unsigned mwGrab( unsigned kb ) {
1242     TESTS(NULL,0);
1243     return mwGrab_( kb, MW_VAL_GRB, 0 );
1244     }
1245 
mwDrop(unsigned kb)1246 unsigned mwDrop( unsigned kb ) {
1247     TESTS(NULL,0);
1248     return mwDrop_( kb, MW_VAL_GRB, 0 );
1249     }
1250 
mwDropAll()1251 static void mwDropAll() {
1252     TESTS(NULL,0);
1253     (void) mwDrop_( 0, MW_VAL_GRB, 0 );
1254     (void) mwDrop_( 0, MW_VAL_NML, 0 );
1255     if( mwGrabList != NULL )
1256         mwWrite( "internal: the grab list is not empty after mwDropAll()\n");
1257     }
1258 
mwGrabType(int type)1259 static const char *mwGrabType( int type ) {
1260     switch( type ) {
1261         case MW_VAL_GRB:
1262             return "grabbed";
1263         case MW_VAL_NML:
1264             return "no-mans-land";
1265         default:
1266             /* do nothing */
1267             ;
1268         }
1269     return "<unknown type>";
1270     }
1271 
mwGrab_(unsigned kb,int type,int silent)1272 static unsigned mwGrab_( unsigned kb, int type, int silent ) {
1273     unsigned i = kb;
1274     mwGrabData *gd;
1275     if( !kb ) i = kb = 65000U;
1276 
1277     for(;kb;kb--) {
1278         if( mwUseLimit &&
1279             (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) {
1280             if( !silent ) {
1281                 mwWrite("grabbed: all allowed memory to %s (%u kb)\n",
1282                     mwGrabType(type), i-kb);
1283                 FLUSH();
1284                 }
1285             return i-kb;
1286             }
1287         gd = (mwGrabData*) malloc( sizeof(mwGrabData) );
1288         if( gd == NULL ) {
1289             if( !silent ) {
1290                 mwWrite("grabbed: all available memory to %s (%u kb)\n",
1291                     mwGrabType(type), i-kb);
1292                 FLUSH();
1293                 }
1294             return i-kb;
1295             }
1296         mwGrabSize += (long) sizeof(mwGrabData);
1297         gd->next = mwGrabList;
1298         memset( gd->blob, type, sizeof(gd->blob) );
1299         gd->type = type;
1300         mwGrabList = gd;
1301         }
1302     if( !silent ) {
1303         mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) );
1304         FLUSH();
1305         }
1306     return i;
1307     }
1308 
mwDrop_(unsigned kb,int type,int silent)1309 static unsigned mwDrop_( unsigned kb, int type, int silent ) {
1310     unsigned i = kb;
1311     mwGrabData *gd,*tmp,*pr;
1312     const void *p;
1313 
1314     if( mwGrabList == NULL && kb == 0 ) return 0;
1315     if( !kb ) i = kb = 60000U;
1316 
1317     pr = NULL;
1318     gd = mwGrabList;
1319     for(;kb;) {
1320         if( gd == NULL ) {
1321             if( i-kb > 0 && !silent ) {
1322                 mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb);
1323                 FLUSH();
1324                 }
1325             return i-kb;
1326             }
1327         if( gd->type == type ) {
1328             if( pr ) pr->next = gd->next;
1329             kb --;
1330             tmp = gd;
1331             if( mwGrabList == gd ) mwGrabList = gd->next;
1332             gd = gd->next;
1333             p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type );
1334             if( p != NULL ) {
1335                 mwWrite( "wild pointer: <%ld> %s memory hit at %p\n",
1336                     mwCounter, mwGrabType(type), p );
1337                 FLUSH();
1338                 }
1339             mwGrabSize -= (long) sizeof(mwGrabData);
1340             free( tmp );
1341             }
1342         else {
1343             pr = gd;
1344             gd = gd->next;
1345             }
1346         }
1347     if( !silent ) {
1348         mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) );
1349         FLUSH();
1350         }
1351     return i;
1352     }
1353 
1354 /***********************************************************************
1355 ** No-Mans-Land
1356 ***********************************************************************/
1357 
mwNoMansLand(int level)1358 void mwNoMansLand( int level ) {
1359     mwAutoInit();
1360     TESTS(NULL,0);
1361     switch( level ) {
1362         case MW_NML_NONE:
1363             (void) mwDrop_( 0, MW_VAL_NML, 0 );
1364             break;
1365         case MW_NML_FREE:
1366             break;
1367         case MW_NML_ALL:
1368             (void) mwGrab_( 0, MW_VAL_NML, 0 );
1369             break;
1370         default:
1371             return;
1372         }
1373     mwNML = level;
1374     }
1375 
1376 /***********************************************************************
1377 ** Static functions
1378 ***********************************************************************/
1379 
mwAutoInit(void)1380 static void mwAutoInit( void )
1381 {
1382     if( mwInited ) return;
1383     mwUseAtexit = 1;
1384     mwInit();
1385     return;
1386 }
1387 
mwLogR()1388 static FILE *mwLogR() {
1389     if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog;
1390     if( mwLog == mwLogB1 ) mwLogB2 = mwLog;
1391     if( mwLog == mwLogB2 ) mwLogB1 = mwLog;
1392     if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1;
1393     if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) {
1394         mwWrite("internal: log file handle damaged and recovered\n");
1395         FLUSH();
1396         return mwLog;
1397         }
1398     fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" );
1399     mwLog = mwLogB1 = mwLogB2 = mwSTDERR;
1400     return mwSTDERR;
1401     }
1402 
mwLogW(FILE * p)1403 static void mwLogW( FILE *p ) {
1404     mwLog = mwLogB1 = mwLogB2 = p;
1405     }
1406 
mwFlushR()1407 static int mwFlushR() {
1408     if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing;
1409     if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing;
1410     if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing;
1411     if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1;
1412     if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) {
1413         mwWrite("internal: flushing flag damaged and recovered\n");
1414         FLUSH();
1415         return mwFlushing;
1416         }
1417     mwWrite("internal: flushing flag destroyed, so set to true\n");
1418     mwFlushing = mwFlushingB1 = mwFlushingB2 = 1;
1419     return 1;
1420     }
1421 
mwFlushW(int n)1422 static void mwFlushW( int n ) {
1423     mwFlushing = mwFlushingB1 = mwFlushingB2 = n;
1424     }
1425 
mwIncErr()1426 static void mwIncErr() {
1427     mwErrors++;
1428     mwFlushW( mwFlushR()+1 );
1429     FLUSH();
1430     }
1431 
mwFlush()1432 static void mwFlush() {
1433     if( mwLogR() == NULL ) return;
1434 #ifdef MW_FLUSH
1435     fflush( mwLogR() );
1436 #else
1437     if( mwFlushR() ) fflush( mwLogR() );
1438 #endif
1439     return;
1440     }
1441 
mwUnlink(mwData * mw,const char * file,int line)1442 static void mwUnlink( mwData* mw, const char* file, int line ) {
1443     if( mw->prev == NULL ) {
1444         if( mwHead != mw )
1445             mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n",
1446                 mwCounter, file, line, mw );
1447         mwHead = mw->next;
1448         }
1449     else {
1450         if( mw->prev->next != mw )
1451             mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n",
1452                 mwCounter, file, line, mw );
1453         else mw->prev->next = mw->next;
1454         }
1455     if( mw->next == NULL ) {
1456         if( mwTail != mw )
1457             mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n",
1458                 mwCounter, file, line, mw );
1459         mwTail = mw->prev;
1460         }
1461     else {
1462         if( mw->next->prev != mw )
1463             mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n",
1464                 mwCounter, file, line, mw );
1465         else mw->next->prev = mw->prev;
1466         }
1467     }
1468 
1469 /*
1470 ** Relinking tries to repair a damaged mw block.
1471 ** Returns nonzero if it thinks it successfully
1472 ** repaired the heap chain.
1473 */
mwRelink(mwData * mw,const char * file,int line)1474 static int mwRelink( mwData* mw, const char* file, int line ) {
1475     int fails;
1476     mwData *mw1, *mw2;
1477     long count, size;
1478     mwStat *ms;
1479 
1480 	if( file == NULL ) file = "unknown";
1481 
1482     if( mw == NULL ) {
1483         mwWrite("relink: cannot repair MW at NULL\n");
1484         FLUSH();
1485         goto emergency;
1486         }
1487 
1488     if( !mwIsSafeAddr(mw, mwDataSize) ) {
1489         mwWrite("relink: MW-%p is a garbage pointer\n");
1490         FLUSH();
1491         goto emergency;
1492         }
1493 
1494     mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw );
1495     FLUSH();
1496     fails = 0;
1497 
1498     /* Repair from head */
1499     if( mwHead != mw ) {
1500         if( !mwIsSafeAddr( mwHead, mwDataSize ) ) {
1501             mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw );
1502             FLUSH();
1503             goto emergency;
1504             }
1505         for( mw1=mwHead; mw1; mw1=mw1->next ) {
1506             if( mw1->next == mw ) {
1507                 mw->prev = mw1;
1508                 break;
1509                 }
1510             if( mw1->next &&
1511                 ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) {
1512                 mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next );
1513                 FLUSH();
1514                 goto emergency;
1515                 }
1516             }
1517         if( mw1 == NULL ) {
1518             mwWrite("relink: MW-%p not found in forward chain search\n", mw );
1519             FLUSH();
1520             fails ++;
1521             }
1522         }
1523 	else
1524 	{
1525 		mwWrite( "relink: MW-%p is the head (first) allocation\n", mw );
1526 		if( mw->prev != NULL )
1527 		{
1528 			mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw );
1529 			mw->prev = NULL;
1530 		}
1531 	}
1532 
1533     /* Repair from tail */
1534     if( mwTail != mw ) {
1535         if( !mwIsSafeAddr( mwTail, mwDataSize ) ) {
1536             mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw );
1537             FLUSH();
1538             goto emergency;
1539             }
1540         for( mw1=mwTail; mw1; mw1=mw1->prev ) {
1541             if( mw1->prev == mw ) {
1542                 mw->next = mw1;
1543                 break;
1544                 }
1545             if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) {
1546                 mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev );
1547                 FLUSH();
1548                 goto emergency;
1549                 }
1550             }
1551         if( mw1 == NULL ) {
1552             mwWrite("relink: MW-%p not found in reverse chain search\n", mw );
1553             FLUSH();
1554             fails ++;
1555             }
1556         }
1557 	else
1558 	{
1559 		mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw );
1560 		if( mw->next != NULL )
1561 		{
1562 			mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw );
1563 			mw->next = NULL;
1564 		}
1565 	}
1566 
1567     if( fails > 1 ) {
1568         mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw );
1569         FLUSH();
1570         goto verifyok;
1571         }
1572 
1573     /* restore MW info where possible */
1574     if( mwIsReadAddr( mw->file, 1 ) ) {
1575         ms = mwStatGet( mw->file, -1, 0 );
1576         if( ms == NULL ) mw->file = "<relinked>";
1577         }
1578     mw->check = CHKVAL(mw);
1579     goto verifyok;
1580 
1581     /* Emergency repair */
1582     emergency:
1583 
1584     if( mwHead == NULL && mwTail == NULL )
1585     {
1586         if( mwStatCurAlloc == 0 )
1587             mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line );
1588         else
1589             mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line );
1590         FLUSH();
1591         return 0;
1592     }
1593 
1594     mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line );
1595     FLUSH();
1596 
1597 	if( mwHead == NULL || mwTail == NULL )
1598 	{
1599 		if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail );
1600 		else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead );
1601 	}
1602 
1603     mw1=NULL;
1604     if( mwHead != NULL )
1605 	{
1606 		if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) )
1607 		{
1608 			mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead );
1609 			mwHead = NULL;
1610 			goto scan_reverse;
1611 		}
1612 		if( mwHead->prev != NULL )
1613 		{
1614 			mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev );
1615 		}
1616         for( mw1=mwHead; mw1; mw1=mw1->next )
1617 		{
1618 			if( mw1->next )
1619 			{
1620 				if( !mwIsReadAddr(mw1->next,mwDataSize) ||
1621 					!mw1->next->check != CHKVAL(mw1) ||
1622 					mw1->next->prev != mw1 )
1623 				{
1624 					mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
1625 						mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line );
1626 					if( mwIsReadAddr(mw1->next,mwDataSize ) )
1627 					{
1628 						mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
1629 							mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"",
1630 							mwIsReadAddr(mw1->file,16)?mw1->file:"<garbage-pointer>", mw1->line );
1631 					}
1632 					else
1633 					{
1634 						mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n",
1635 							mw1->next );
1636 					}
1637 					break;
1638 				}
1639 			}
1640         }
1641 	}
1642 
1643 
1644 scan_reverse:
1645     mw2=NULL;
1646     if( mwTail != NULL )
1647 	{
1648 		if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) )
1649 		{
1650 			mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail );
1651 			mwTail = NULL;
1652 			goto analyze;
1653 		}
1654 		if( mwTail->next != NULL )
1655 		{
1656 			mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next );
1657 		}
1658         for( mw2=mwTail; mw2; mw2=mw2->prev )
1659 		{
1660             if( mw2->prev )
1661 			{
1662 				if( !mwIsReadAddr(mw2->prev,mwDataSize) ||
1663 					!mw2->prev->check != CHKVAL(mw2) ||
1664 					mw2->prev->next != mw2 )
1665 				{
1666 					mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
1667 						mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line );
1668 					if( mwIsReadAddr(mw2->prev,mwDataSize ) )
1669 					{
1670 						mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
1671 							mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"",
1672 							mwIsReadAddr(mw2->file,16)?mw2->file:"<garbage-pointer>", mw2->line );
1673 					}
1674 					else
1675 					{
1676 						mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n",
1677 							mw2->prev );
1678 					}
1679 					break;
1680 				}
1681 			}
1682         }
1683 	}
1684 
1685 analyze:
1686 	if( mwHead == NULL && mwTail == NULL )
1687 	{
1688         mwWrite("relink: both head and tail pointers damaged, aborting program\n");
1689         mwFlushW(1);
1690         FLUSH();
1691         abort();
1692 	}
1693 	if( mwHead == NULL )
1694 	{
1695 		mwHead = mw2;
1696 		mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 );
1697 		mw2->prev = NULL;
1698 		mw1 = mw2 = NULL;
1699 	}
1700 	if( mwTail == NULL )
1701 	{
1702 		mwTail = mw1;
1703 		mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 );
1704 		mw1->next = NULL;
1705 		mw1 = mw2 = NULL;
1706 	}
1707     if( mw1 == NULL && mw2 == NULL &&
1708         mwHead->prev == NULL && mwTail->next == NULL ) {
1709         mwWrite("relink: verifying heap integrity...\n" );
1710         FLUSH();
1711         goto verifyok;
1712         }
1713     if( mw1 && mw2 && mw1 != mw2 ) {
1714         mw1->next = mw2;
1715         mw2->prev = mw1;
1716         mwWrite("relink: emergency repairs successful, assessing damage...\n");
1717         FLUSH();
1718         }
1719     else {
1720         mwWrite("relink: heap totally destroyed, aborting program\n");
1721         mwFlushW(1);
1722         FLUSH();
1723         abort();
1724         }
1725 
1726     /* Verify by checking that the number of active allocations */
1727     /* match the number of entries in the chain */
1728 verifyok:
1729     if( !mwIsHeapOK( NULL ) ) {
1730         mwWrite("relink: heap verification FAILS - aborting program\n");
1731         mwFlushW(1);
1732         FLUSH();
1733         abort();
1734         }
1735     for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) {
1736         count ++;
1737         size += (long) mw1->size;
1738         }
1739     if( count == mwNumCurAlloc ) {
1740         mwWrite("relink: successful, ");
1741         if( size == mwStatCurAlloc ) {
1742             mwWrite("no allocations lost\n");
1743             }
1744         else {
1745             if( mw != NULL ) {
1746                 mwWrite("size information lost for MW-%p\n", mw);
1747                 mw->size = 0;
1748                 }
1749             }
1750         }
1751     else {
1752         mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n",
1753 			mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size );
1754         return 0;
1755         }
1756 
1757     return 1;
1758     }
1759 
1760 /*
1761 **  If mwData* is NULL:
1762 **      Returns 0 if heap chain is broken.
1763 **      Returns 1 if heap chain is intact.
1764 **  If mwData* is not NULL:
1765 **      Returns 0 if mwData* is missing or if chain is broken.
1766 **      Returns 1 if chain is intact and mwData* is found.
1767 */
mwIsHeapOK(mwData * includes_mw)1768 static int mwIsHeapOK( mwData *includes_mw ) {
1769     int found = 0;
1770     mwData *mw;
1771 
1772     for( mw = mwHead; mw; mw=mw->next ) {
1773         if( includes_mw == mw ) found++;
1774         if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
1775         if( mw->prev ) {
1776             if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0;
1777             if( mw==mwHead || mw->prev->next != mw ) return 0;
1778             }
1779         if( mw->next ) {
1780             if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0;
1781             if( mw==mwTail || mw->next->prev != mw ) return 0;
1782             }
1783         else if( mw!=mwTail ) return 0;
1784         }
1785 
1786     if( includes_mw != NULL && !found ) return 0;
1787 
1788     return 1;
1789     }
1790 
mwIsOwned(mwData * mw,const char * file,int line)1791 static int mwIsOwned( mwData* mw, const char *file, int line ) {
1792     int retv;
1793     mwStat *ms;
1794 
1795     /* see if the address is legal according to OS */
1796     if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
1797 
1798     /* make sure we have _anything_ allocated */
1799     if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 )
1800         return 0;
1801 
1802     /* calculate checksum */
1803     if( mw->check != CHKVAL(mw) ) {
1804         /* may be damaged checksum, see if block is in heap */
1805         if( mwIsHeapOK( mw ) ) {
1806             /* damaged checksum, repair it */
1807             mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n",
1808                 mwCounter, file, line, mw );
1809             mwIncErr();
1810             if( mwIsReadAddr( mw->file, 1 ) ) {
1811                 ms = mwStatGet( mw->file, -1, 0 );
1812                 if( ms == NULL ) mw->file = "<relinked>";
1813                 }
1814             else mw->file = "<unknown>";
1815             mw->size = 0;
1816             mw->check = CHKVAL(mw);
1817             return 1;
1818             }
1819         /* no, it's just some garbage data */
1820         return 0;
1821         }
1822 
1823 	/* check that the non-NULL pointers are safe */
1824 	if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line );
1825 	if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line );
1826 
1827     /* safe address, checksum OK, proceed with heap checks */
1828 
1829     /* see if the block is in the heap */
1830     retv = 0;
1831     if( mw->prev ) { if( mw->prev->next == mw ) retv ++; }
1832     else { if( mwHead == mw ) retv++; }
1833     if( mw->next ) { if( mw->next->prev == mw ) retv ++; }
1834     else { if( mwTail == mw ) retv++; }
1835     if( mw->check == CHKVAL(mw) ) retv ++;
1836     if( retv > 2 ) return 1;
1837 
1838     /* block not in heap, check heap for corruption */
1839 
1840     if( !mwIsHeapOK( mw ) ) {
1841         if( mwRelink( mw, file, line ) )
1842             return 1;
1843         }
1844 
1845     /* unable to repair */
1846     mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n",
1847        mwCounter, file, line, mw );
1848     mwIncErr();
1849 
1850     return 0;
1851     }
1852 
1853 /*
1854 ** mwTestBuf:
1855 **  Checks a buffers links and pre/postfixes.
1856 **  Writes errors found to the log.
1857 **  Returns zero if no errors found.
1858 */
mwTestBuf(mwData * mw,const char * file,int line)1859 static int mwTestBuf( mwData* mw, const char* file, int line ) {
1860     int retv = 0;
1861     char *p;
1862 
1863     if( file == NULL ) file = "unknown";
1864 
1865     if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) {
1866         mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n",
1867             mwCounter, file, line, mw );
1868         mwIncErr();
1869         return 2;
1870         }
1871 
1872     if( mw->check != CHKVAL(mw) ) {
1873         mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n",
1874             mwCounter, file, line );
1875         mwIncErr();
1876         if( !mwRelink( mw, file, line ) ) return 2;
1877         }
1878 
1879     if( mw->prev && mw->prev->next != mw ) {
1880         mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n",
1881             mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
1882         mwIncErr();
1883         if( !mwRelink( mw, file, line ) ) retv = 2;
1884         }
1885     if( mw->next && mw->next->prev != mw ) {
1886         mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n",
1887             mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
1888         mwIncErr();
1889         if( !mwRelink( mw, file, line ) ) retv = 2;
1890         }
1891 
1892     p = ((char*)mw) + mwDataSize;
1893     if( mwCheckOF( p ) ) {
1894         mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
1895             mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
1896         mwIncErr();
1897         retv = 1;
1898         }
1899     p += mwOverflowZoneSize + mw->size;
1900     if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) {
1901         mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
1902             mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
1903         mwIncErr();
1904         retv = 1;
1905         }
1906 
1907     return retv;
1908     }
1909 
mwDefaultOutFunc(int c)1910 static void mwDefaultOutFunc( int c ) {
1911     if( mwLogR() ) fputc( c, mwLogR() );
1912     }
1913 
mwWrite(const char * format,...)1914 static void mwWrite( const char *format, ... ) {
1915     int tot, oflow = 0;
1916     va_list mark;
1917     mwAutoInit();
1918     if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
1919     va_start( mark, format );
1920     tot = vsprintf( mwPrintBuf, format, mark );
1921     va_end( mark );
1922     if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
1923     for(tot=0;mwPrintBuf[tot];tot++)
1924         (*mwOutFunction)( mwPrintBuf[tot] );
1925     if( oflow ) {
1926         mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 );
1927         FLUSH();
1928         }
1929     return;
1930     }
1931 
mwLogFile(const char * name)1932 static void mwLogFile( const char *name ) {
1933     time_t tid;
1934     (void) time( &tid );
1935     if( mwLogR() != NULL ) {
1936         fclose( mwLogR() );
1937         mwLogW( NULL );
1938         }
1939     if( name == NULL ) return;
1940     mwLogW( fopen( name, "a" COMMIT ) );
1941     if( mwLogR() == NULL )
1942         mwWrite( "logfile: failed to open/create file '%s'\n", name );
1943     }
1944 
1945 /*
1946 ** Try to free NML memory until a contiguous allocation of
1947 ** 'needed' bytes can be satisfied. If this is not enough
1948 ** and the 'urgent' parameter is nonzero, grabbed memory is
1949 ** also freed.
1950 */
mwFreeUp(size_t needed,int urgent)1951 static size_t mwFreeUp( size_t needed, int urgent ) {
1952     void *p;
1953     mwData *mw, *mw2;
1954     char *data;
1955 
1956     /* free grabbed NML memory */
1957     for(;;) {
1958         if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break;
1959         p = malloc( needed );
1960         if( p == NULL ) continue;
1961         free( p );
1962         return needed;
1963         }
1964 
1965     /* free normal NML memory */
1966     mw = mwHead;
1967     while( mw != NULL ) {
1968         if( !(mw->flag & MW_NML) ) mw = mw->next;
1969         else {
1970             data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
1971             if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
1972                 mwIncErr();
1973                 mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
1974                     mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
1975                 }
1976             mw2 = mw->next;
1977             mwUnlink( mw, "mwFreeUp", 0 );
1978             free( mw );
1979             mw = mw2;
1980             p = malloc( needed );
1981             if( p == NULL ) continue;
1982             free( p );
1983             return needed;
1984             }
1985         }
1986 
1987     /* if not urgent (for internal purposes), fail */
1988     if( !urgent ) return 0;
1989 
1990     /* free grabbed memory */
1991     for(;;) {
1992         if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break;
1993         p = malloc( needed );
1994         if( p == NULL ) continue;
1995         free( p );
1996         return needed;
1997         }
1998 
1999     return 0;
2000     }
2001 
mwTestMem(const void * p,unsigned len,int c)2002 static const void * mwTestMem( const void *p, unsigned len, int c ) {
2003     const unsigned char *ptr;
2004     ptr = (const unsigned char *) p;
2005     while( len-- ) {
2006         if( *ptr != (unsigned char)c ) return (const void*)ptr;
2007         ptr ++;
2008         }
2009     return NULL;
2010     }
2011 
mwStrCmpI(const char * s1,const char * s2)2012 static int mwStrCmpI( const char *s1, const char *s2 ) {
2013     if( s1 == NULL || s2 == NULL ) return 0;
2014     while( *s1 ) {
2015         if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; }
2016         return 1;
2017         }
2018     return 0;
2019     }
2020 
2021 #define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; }
2022 
mwTestNow(const char * file,int line,int always_invoked)2023 static int mwTestNow( const char *file, int line, int always_invoked ) {
2024     int retv = 0;
2025     mwData *mw;
2026     char *data;
2027 
2028     if( file && !always_invoked )
2029         mwWrite("check: <%ld> %s(%d), checking %s%s%s\n",
2030             mwCounter, file, line,
2031 			(mwTestFlags & MW_TEST_CHAIN) ? "chain ": "",
2032 		    (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "",
2033 		    (mwTestFlags & MW_TEST_NML) ? "nomansland ": ""
2034 			);
2035 
2036     if( mwTestFlags & MW_TEST_CHAIN ) {
2037         for( mw = mwHead; mw; mw=mw->next ) {
2038 			if( !mwIsSafeAddr(mw, mwDataSize) ) {
2039 				AIPH();
2040 				mwWrite("check: heap corruption detected\n");
2041 				mwIncErr();
2042 				return retv + 1;
2043 				}
2044 			if( mw->prev ) {
2045 				if( !mwIsSafeAddr(mw->prev, mwDataSize) ) {
2046 					AIPH();
2047 					mwWrite("check: heap corruption detected\n");
2048 					mwIncErr();
2049 					return retv + 1;
2050 					}
2051 				if( mw==mwHead || mw->prev->next != mw ) {
2052 					AIPH();
2053 					mwWrite("check: heap chain broken, prev link incorrect\n");
2054 					mwIncErr();
2055 					retv ++;
2056 					}
2057 				}
2058 			if( mw->next ) {
2059 				if( !mwIsSafeAddr(mw->next, mwDataSize) ) {
2060 					AIPH();
2061 					mwWrite("check: heap corruption detected\n");
2062 					mwIncErr();
2063 					return retv + 1;
2064 					}
2065 				if( mw==mwTail || mw->next->prev != mw ) {
2066 					AIPH();
2067 					mwWrite("check: heap chain broken, next link incorrect\n");
2068 					mwIncErr();
2069 					retv ++;
2070 					}
2071 				}
2072 			else if( mw!=mwTail ) {
2073 				AIPH();
2074 				mwWrite("check: heap chain broken, tail incorrect\n");
2075 				mwIncErr();
2076 				retv ++;
2077 				}
2078             }
2079         }
2080     if( mwTestFlags & MW_TEST_ALLOC ) {
2081         for( mw = mwHead; mw; mw=mw->next ) {
2082             if( mwTestBuf( mw, file, line ) ) retv ++;
2083             }
2084         }
2085     if( mwTestFlags & MW_TEST_NML ) {
2086         for( mw = mwHead; mw; mw=mw->next ) {
2087             if( (mw->flag & MW_NML) ) {
2088                 data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
2089                 if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
2090                     mwIncErr();
2091                     mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
2092                         mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
2093                     }
2094                 }
2095             }
2096         }
2097 
2098 
2099 	if( file && !always_invoked && !retv )
2100         mwWrite("check: <%ld> %s(%d), complete; no errors\n",
2101             mwCounter, file, line );
2102     return retv;
2103     }
2104 
2105 /**********************************************************************
2106 ** Statistics
2107 **********************************************************************/
2108 
mwStatReport()2109 static void mwStatReport()
2110 {
2111     mwStat* ms, *ms2;
2112     const char *modname;
2113     int modnamelen;
2114 
2115     /* global statistics report */
2116     mwWrite( "\nMemory usage statistics (global):\n" );
2117     mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc );
2118     mwWrite( " L)argest memory usage      : %ld\n", mwStatMaxAlloc );
2119     mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc );
2120     mwWrite( " U)nfreed bytes totals      : %ld\n", mwStatCurAlloc );
2121     FLUSH();
2122 
2123     if( mwStatLevel < 1 ) return;
2124 
2125     /* on a per-module basis */
2126     mwWrite( "\nMemory usage statistics (detailed):\n");
2127     mwWrite( " Module/Line                                Number   Largest  Total    Unfreed \n");
2128     for( ms=mwStatList; ms; ms=ms->next )
2129     {
2130         if( ms->line == -1 )
2131         {
2132 			if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = "<unknown>";
2133 			else modname = ms->file;
2134 			modnamelen = strlen(modname);
2135 			if( modnamelen > 42 )
2136 			{
2137 				modname = modname + modnamelen - 42;
2138 			}
2139 
2140             mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n",
2141             	modname, ms->num, ms->max, ms->total, ms->curr );
2142             if( ms->file && mwStatLevel > 1 )
2143             {
2144                 for( ms2=mwStatList; ms2; ms2=ms2->next )
2145                 {
2146                     if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) )
2147 					{
2148 					mwWrite( "  %-8d                                  %-8ld %-8ld %-8ld %-8ld\n",
2149 						ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr );
2150 					}
2151 				}
2152 			}
2153 		}
2154 	}
2155 }
2156 
mwStatGet(const char * file,int line,int makenew)2157 static mwStat* mwStatGet( const char *file, int line, int makenew ) {
2158     mwStat* ms;
2159 
2160     if( mwStatLevel < 2 ) line = -1;
2161 
2162     for( ms=mwStatList; ms!=NULL; ms=ms->next ) {
2163         if( line != ms->line ) continue;
2164         if( file==NULL ) {
2165             if( ms->file == NULL ) break;
2166             continue;
2167             }
2168         if( ms->file == NULL ) continue;
2169         if( !strcmp( ms->file, file ) ) break;
2170         }
2171 
2172     if( ms != NULL ) return ms;
2173 
2174     if( !makenew ) return NULL;
2175 
2176     ms = (mwStat*) malloc( sizeof(mwStat) );
2177     if( ms == NULL ) {
2178         if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) ||
2179             (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) {
2180             mwWrite("internal: memory low, statistics incomplete for '%s'\n", file );
2181             return NULL;
2182             }
2183         }
2184     ms->file = file;
2185     ms->line = line;
2186     ms->total = 0L;
2187     ms->max = 0L;
2188     ms->num = 0L;
2189     ms->curr = 0L;
2190     ms->next = mwStatList;
2191     mwStatList = ms;
2192     return ms;
2193     }
2194 
mwStatAlloc(size_t size,const char * file,int line)2195 static void mwStatAlloc( size_t size, const char* file, int line ) {
2196     mwStat* ms;
2197 
2198     /* update the module statistics */
2199     ms = mwStatGet( file, -1, 1 );
2200     if( ms != NULL ) {
2201         ms->total += (long) size;
2202         ms->curr += (long) size;
2203         ms->num ++;
2204         if( ms->curr > ms->max ) ms->max = ms->curr;
2205         }
2206 
2207     /* update the line statistics */
2208     if( mwStatLevel > 1 && line != -1 && file ) {
2209         ms = mwStatGet( file, line, 1 );
2210         if( ms != NULL ) {
2211             ms->total += (long) size;
2212             ms->curr += (long) size;
2213             ms->num ++;
2214             if( ms->curr > ms->max ) ms->max = ms->curr;
2215             }
2216         }
2217 
2218     }
2219 
mwStatFree(size_t size,const char * file,int line)2220 static void mwStatFree( size_t size, const char* file, int line ) {
2221     mwStat* ms;
2222 
2223     /* update the module statistics */
2224     ms = mwStatGet( file, -1, 1 );
2225     if( ms != NULL ) ms->curr -= (long) size;
2226 
2227     /* update the line statistics */
2228     if( mwStatLevel > 1 && line != -1 && file ) {
2229         ms = mwStatGet( file, line, 1 );
2230         if( ms != NULL ) ms->curr -= (long) size;
2231         }
2232     }
2233 
2234 /***********************************************************************
2235 ** Safe memory checkers
2236 **
2237 ** Using ifdefs, implement the operating-system specific mechanism
2238 ** of identifying a piece of memory as legal to access with read
2239 ** and write priviliges. Default: return nonzero for non-NULL pointers.
2240 ***********************************************************************/
2241 
mwDummy(char c)2242 static char mwDummy( char c )
2243 {
2244 	return c;
2245 }
2246 
2247 #ifndef MW_SAFEADDR
2248 #ifdef WIN32
2249 #define MW_SAFEADDR
2250 #define WIN32_LEAN_AND_MEAN
2251 #include <windows.h>
mwIsReadAddr(const void * p,unsigned len)2252 int mwIsReadAddr( const void *p, unsigned len )
2253 {
2254     if( p == NULL ) return 0;
2255     if( IsBadReadPtr(p,len) ) return 0;
2256     return 1;
2257 }
mwIsSafeAddr(void * p,unsigned len)2258 int mwIsSafeAddr( void *p, unsigned len )
2259 {
2260     /* NOTE: For some reason, under Win95 the IsBad... */
2261     /* can return false for invalid pointers. */
2262     if( p == NULL ) return 0;
2263     if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0;
2264     return 1;
2265 }
2266 #endif /* WIN32 */
2267 #endif /* MW_SAFEADDR */
2268 
2269 #ifndef MW_SAFEADDR
2270 #ifdef SIGSEGV
2271 #define MW_SAFEADDR
2272 
2273 typedef void (*mwSignalHandlerPtr)( int );
2274 mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0;
2275 jmp_buf mwSIGSEGVjump;
2276 static void mwSIGSEGV( int n );
2277 
mwSIGSEGV(int n)2278 static void mwSIGSEGV( int n )
2279 {
2280 	longjmp( mwSIGSEGVjump, 1 );
2281 }
2282 
mwIsReadAddr(const void * p,unsigned len)2283 int mwIsReadAddr( const void *p, unsigned len )
2284 {
2285 	const char *ptr;
2286 
2287     if( p == NULL ) return 0;
2288 	if( !len ) return 1;
2289 
2290 	/* set up to catch the SIGSEGV signal */
2291 	mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
2292 
2293 	if( setjmp( mwSIGSEGVjump ) )
2294 	{
2295 		signal( SIGSEGV, mwOldSIGSEGV );
2296 		return 0;
2297 	}
2298 
2299 	/* read all the bytes in the range */
2300 	ptr = (const char *)p;
2301 	ptr += len;
2302 
2303 	/* the reason for this rather strange construct is that */
2304 	/* we want to keep the number of used parameters and locals */
2305 	/* to a minimum. if we use len for a counter gcc will complain */
2306 	/* it may get clobbered by longjmp() at high warning levels. */
2307 	/* it's a harmless warning, but this way we don't have to see it. */
2308 	do
2309 	{
2310 		ptr --;
2311 		if( *ptr == 0x7C ) (void) mwDummy( (char)0 );
2312 	} while( (const void*) ptr != p );
2313 
2314 	/* remove the handler */
2315 	signal( SIGSEGV, mwOldSIGSEGV );
2316 
2317     return 1;
2318 }
mwIsSafeAddr(void * p,unsigned len)2319 int mwIsSafeAddr( void *p, unsigned len )
2320 {
2321 	char *ptr;
2322 
2323 	if( p == NULL ) return 0;
2324 	if( !len ) return 1;
2325 
2326 	/* set up to catch the SIGSEGV signal */
2327 	mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
2328 
2329 	if( setjmp( mwSIGSEGVjump ) )
2330 	{
2331 		signal( SIGSEGV, mwOldSIGSEGV );
2332 		return 0;
2333 	}
2334 
2335 	/* read and write-back all the bytes in the range */
2336 	ptr = (char *)p;
2337 	ptr += len;
2338 
2339 	/* the reason for this rather strange construct is that */
2340 	/* we want to keep the number of used parameters and locals */
2341 	/* to a minimum. if we use len for a counter gcc will complain */
2342 	/* it may get clobbered by longjmp() at high warning levels. */
2343 	/* it's a harmless warning, but this way we don't have to see it. */
2344 	do
2345 	{
2346 		ptr --;
2347 		*ptr = mwDummy( *ptr );
2348 	} while( (void*) ptr != p );
2349 
2350 	/* remove the handler */
2351 	signal( SIGSEGV, mwOldSIGSEGV );
2352 
2353     return 1;
2354 }
2355 #endif /* SIGSEGV */
2356 #endif /* MW_SAFEADDR */
2357 
2358 #ifndef MW_SAFEADDR
mwIsReadAddr(const void * p,unsigned len)2359 int mwIsReadAddr( const void *p, unsigned len )
2360 {
2361     if( p == NULL ) return 0;
2362     if( len == 0 ) return 1;
2363     return 1;
2364 }
mwIsSafeAddr(void * p,unsigned len)2365 int mwIsSafeAddr( void *p, unsigned len )
2366 {
2367     if( p == NULL ) return 0;
2368     if( len == 0 ) return 1;
2369     return 1;
2370 }
2371 #endif
2372 
2373 /**********************************************************************
2374 ** C++ new & delete
2375 **********************************************************************/
2376 
2377 #if 0 /* 980317: disabled C++ */
2378 
2379 #ifdef __cplusplus
2380 #ifndef MEMWATCH_NOCPP
2381 
2382 int mwNCur = 0;
2383 const char *mwNFile = NULL;
2384 int mwNLine = 0;
2385 
2386 class MemWatch {
2387 public:
2388     MemWatch();
2389     ~MemWatch();
2390     };
2391 
2392 MemWatch::MemWatch() {
2393     if( mwInited ) return;
2394     mwUseAtexit = 0;
2395     mwInit();
2396     }
2397 
2398 MemWatch::~MemWatch() {
2399     if( mwUseAtexit ) return;
2400     mwTerm();
2401     }
2402 
2403 /*
2404 ** This global new will catch all 'new' calls where MEMWATCH is
2405 ** not active.
2406 */
2407 void* operator new( unsigned size ) {
2408     mwNCur = 0;
2409     return mwMalloc( size, "<unknown>", 0 );
2410     }
2411 
2412 /*
2413 ** This is the new operator that's called when a module uses mwNew.
2414 */
2415 void* operator new( unsigned size, const char *file, int line ) {
2416     mwNCur = 0;
2417     return mwMalloc( size, file, line );
2418     }
2419 
2420 /*
2421 ** Since this delete operator will recieve ALL delete's
2422 ** even those from within libraries, we must accept
2423 ** delete's before we've been initialized. Nor can we
2424 ** reliably check for wild free's if the mwNCur variable
2425 ** is not set.
2426 */
2427 void operator delete( void *p ) {
2428     if( p == NULL ) return;
2429     if( !mwInited ) {
2430         free( p );
2431         return;
2432         }
2433     if( mwNCur ) {
2434         mwFree( p, mwNFile, mwNLine );
2435         mwNCur = 0;
2436         return;
2437         }
2438     mwFree_( p );
2439     }
2440 
2441 #endif /* MEMWATCH_NOCPP */
2442 #endif /* __cplusplus */
2443 
2444 #endif /* 980317: disabled C++ */
2445 
2446 /* MEMWATCH.C */
2447