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