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