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