1 /*
2 C K C M D B . C -- malloc debugger.
3 */
4
5 /*
6 Author: Howie Kaye, Columbia University Center for Computing Activities.
7
8 Copyright (C) 1985, 1999,
9 Trustees of Columbia University in the City of New York.
10 All rights reserved. See the C-Kermit COPYING.TXT file or the
11 copyright text in the ckcmai.c module for disclaimer and permissions.
12 */
13 /* Use the real ones in this module! */
14 #ifdef malloc
15 #undef malloc
16 #endif /* malloc */
17 #ifdef calloc
18 #undef calloc
19 #endif /* calloc */
20 #ifdef realloc
21 #undef realloc
22 #endif /* realloc */
23 #ifdef free
24 #undef free
25 #endif /* free */
26
27 #include "ckcsym.h"
28 #include <stdio.h>
29 #include "ckcdeb.h"
30
31 #ifdef COHERENT
32 _PROTOTYP ( FILE * fdopen, (int, char *) );
33 #endif /* COHERENT */
34
35 /*
36 memdebug:
37 variable to control memory debugging.
38 if memdebug == 1, then action is always taken.
39 if memdebug == 0, then no action is taken.
40 if memdebug == -1, then the user is asked (works well with gdb).
41 */
42 int memdebug = -1;
43 int disabled = 0;
44 int inited = 0;
45 /*
46 To use this package, compile your program with:
47 -Dmalloc=dmalloc -Dfree=dfree =Dcalloc=dcalloc ... -DMDEBUG
48 and then link it with ckcmdb.c.
49 */
50 #ifdef MDEBUG
51
52 #ifndef M_SIZE_T
53 #ifdef NEXT
54 #define M_SIZE_T size_t
55 #else
56 #ifdef SUNOS41
57 #define M_SIZE_T unsigned
58 #else
59 #define M_SIZE_T int
60 #endif /* SUNOS41 */
61 #endif /* NEXT */
62 #endif /* M_SIZE_T */
63
64 #ifdef CK_ANSIC
65 _PROTOTYP( void free, (void *) );
66 _PROTOTYP( void * malloc, (size_t) );
67 _PROTOTYP( void * realloc, (void *, size_t) );
68 #else
69 _PROTOTYP( VOID free, (char *) );
70 _PROTOTYP( char * malloc, (M_SIZE_T) );
71 _PROTOTYP( char * realloc, (char *, M_SIZE_T) );
72 #endif /* NEXT */
73
74 _PROTOTYP( VOID m_insert, (char *) );
75 _PROTOTYP( int m_delete, (char *) );
76
77 _PROTOTYP( char * dmalloc, (int) );
78 _PROTOTYP( char * dcalloc, (int, int) );
79 _PROTOTYP( char * drealloc, (char *, int) );
80
81 _PROTOTYP( char *set_range_check, (char *, int) );
82 _PROTOTYP( char *check_range, (char *) );
83 _PROTOTYP( static char *maybe_check_range, (char *) );
84
85 _PROTOTYP( static VOID maybe_quit, (char *) );
86 _PROTOTYP( static int ask, (char *) );
87
88 #ifndef min
89 #define min(x,y) ((x) < (y) ? (x) : (y))
90 #endif /* min */
91 #define RANGE "ABCDEFGHIJKLMNOP"
92 #define INTSIZE sizeof(int)
93 #define LONGSIZE sizeof(long)
94 #define RSIZE sizeof(RANGE)
95 #define RFRONT min((RSIZE/2),LONGSIZE)
96 #define RBACK min((RSIZE-RFRONT),LONGSIZE)
97
98 char *
dmalloc(size)99 dmalloc(size) int size; {
100 char *cp;
101
102 cp = malloc(size + RSIZE + INTSIZE);
103 if (cp) {
104 cp = set_range_check(cp, size);
105 m_insert(cp);
106 }
107 return(cp);
108 }
109
110 char *
dcalloc(nelem,elsize)111 dcalloc(nelem, elsize) int nelem, elsize; {
112 char *cp;
113
114 cp = dmalloc(nelem * elsize);
115 if (cp)
116 memset(cp, 0, nelem * elsize);
117 return(cp);
118 }
119
120 char *
drealloc(bp,size)121 drealloc(bp,size) char *bp; int size; {
122 char *cp;
123
124 if (bp == NULL) {
125 maybe_quit("Freeing NULL pointer");
126 } else {
127 m_delete(bp);
128 cp = check_range(bp);
129 }
130 cp = realloc(cp, size + RSIZE + INTSIZE);
131 if (cp) {
132 cp = set_range_check(cp, size);
133 m_insert(cp);
134 }
135 return(cp);
136 }
137
138 VOID
dfree(cp)139 dfree(cp) char *cp; {
140 if (cp == NULL)
141 maybe_quit("Freeing NULL pointer");
142 else {
143 switch(m_delete(cp)) {
144 case 0:
145 cp = maybe_check_range(cp);
146 break;
147 case 1:
148 cp = check_range(cp);
149 break;
150 case 2:
151 break;
152 }
153 }
154 #ifndef CK_ANSIC
155 return(free(cp));
156 #endif /* CK_ANSIC */
157 }
158
159 char *
set_range_check(cp,size)160 set_range_check(cp,size) char *cp; int size; {
161 register int i;
162 int tmp = size;
163
164 for(i = 0; i < INTSIZE; i++) { /* set the size in the string */
165 cp[i] = tmp & 0xff;
166 tmp >>= 8;
167 }
168 cp += INTSIZE; /* skip the size */
169
170 for(i = 0; i < RFRONT; i++) /* set the front of the range check */
171 cp[i] = RANGE[i]; /* string */
172
173 cp += RFRONT; /* skip the front range check */
174
175 for(i = 0; i < RBACK; i++) /* set the back odf the range check */
176 cp[i+size] = RANGE[i+RFRONT];
177
178 return(cp);
179 }
180
181 /*
182 Put calls to this routine in your code any place where you want to
183 check whether you've copied too many characters into a malloc'd space.
184 */
185 char *
check_range(cp)186 check_range(cp) char *cp; {
187 register char *bp = cp - RFRONT - INTSIZE;
188 char *xp = bp;
189 register int i;
190 int size = 0;
191
192 for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */
193 size <<= 8;
194 size |= bp[INTSIZE-i-1] & 0xff;
195 }
196 bp += INTSIZE;
197
198 for(i = 0; i < RFRONT; i++) /* check front range check */
199 if (bp[i] != RANGE[i]) {
200 maybe_quit("leftside malloc buffer overrun");
201 break;
202 }
203 bp += RFRONT; /* skip front range check */
204
205 for(i = 0; i < RBACK; i++) /* check back range check */
206 if (bp[i+size] != RANGE[i+RFRONT]) {
207 maybe_quit("rightside malloc buffer overrun");
208 break;
209 }
210 return(xp);
211 }
212
213 static char *
maybe_check_range(cp)214 maybe_check_range(cp) char *cp; {
215 register char *bp = cp - RFRONT - INTSIZE;
216 char *xp = bp;
217 register int i;
218 int size = 0;
219
220 for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */
221 size <<= 8;
222 size |= bp[INTSIZE-i-1] & 0xff;
223 }
224 bp += INTSIZE;
225
226 for(i = 0; i < RFRONT; i++) /* check front range check */
227 if (bp[i] != RANGE[i]) {
228 return(cp);
229 }
230 bp += RFRONT; /* skip front range check */
231
232 for(i = 0; i < RBACK; i++) /* check back range check */
233 if (bp[i+size] != RANGE[i+RFRONT]) {
234 fprintf(stderr,"rightside malloc buffer overrun\n");
235 abort();
236 break;
237 }
238 return(xp);
239 }
240
241 #define BUCKETS 10000
242 char *m_used[BUCKETS];
243 char *m_used2[BUCKETS];
244
245 VOID
m_insert(cp)246 m_insert(cp) register char *cp; {
247 register int i;
248
249 if (disabled)
250 return;
251
252 for(i = 0; i < BUCKETS; i++)
253 if (m_used[i] == 0) {
254 m_used[i] = cp;
255 return;
256 }
257 disabled ++;
258 }
259
260 static VOID
m_insert2(cp)261 m_insert2(cp) register char *cp; {
262 register int i;
263
264 if (disabled)
265 return;
266 for(i = 0; i < BUCKETS; i++)
267 if (m_used2[i] == 0) {
268 m_used2[i] = cp;
269 return;
270 }
271 disabled ++;
272 }
273
274 int
m_delete(cp)275 m_delete(cp) register char *cp; {
276 register int i;
277
278 for(i = 0; i < BUCKETS; i++)
279 if (m_used[i] == cp) {
280 m_used[i] = 0;
281 return(1);
282 }
283 for(i = 0; i < BUCKETS; i++)
284 if (m_used2[i] == cp) {
285 m_used2[i] = 0;
286 return(2);
287 }
288 if (disabled)
289 return(0);
290
291 maybe_quit("Freeing unmalloc'ed pointer");
292 return(0);
293 }
294
295 VOID
m_init()296 m_init() {
297 register int i;
298
299 inited = 1;
300 disabled = 0;
301 #ifdef NEXT
302 malloc_debug(2+4+8+16);
303 #endif /* NEXT */
304
305 for(i = 0; i < BUCKETS; i++)
306 m_used[i] = 0;
307 }
308
309 VOID
m_done()310 m_done() {
311 register int i,j=0;
312
313 if (disabled)
314 return;
315 for(i = 0; i < BUCKETS; i++)
316 if (m_used[i] != 0) {
317 if (memdebug) {
318 if (j == 0)
319 fprintf(stderr,"unfree'ed buffers, indices: ");
320 fprintf(stderr,"%d, ", i);
321 j++;
322 }
323 }
324 if (j)
325 fprintf(stderr,"\n");
326 for(i = 0; i < BUCKETS; i++)
327 if (m_used2[i] != 0) {
328 if (memdebug) {
329 if (j == 0)
330 fprintf(stderr,"unfree'ed registered buffers, indices: ");
331 fprintf(stderr,"%d, ", i);
332 j++;
333 }
334 }
335 if (j)
336 fprintf(stderr,"\n");
337 if (j)
338 maybe_quit("Unfree'ed malloc buffers");
339 }
340
341 VOID
m_checkranges()342 m_checkranges() {
343 int i;
344
345 for ( i = 0; i < BUCKETS; i++)
346 if (m_used[i])
347 check_range(m_used[i]);
348 }
349
350 static VOID
maybe_quit(str)351 maybe_quit(str) char *str; {
352 debug(F100,"mdebug maybe_quit","",0);
353 if (memdebug == 0)
354 return;
355 fprintf(stderr,"%s\n",str);
356 if (memdebug == 1)
357 abort();
358 if (memdebug == -1)
359 if (ask("Quit? "))
360 abort();
361 }
362
363 static int
ask(str)364 ask(str) char *str; {
365 char buf[100];
366 FILE *in;
367 int fd;
368
369 fd = dup(fileno(stdin));
370 in = fdopen(fd, "r");
371 while(1) {
372 fprintf(stderr,str);
373 fflush(stderr);
374 if (fgets(buf, 99, in) == NULL) /* EOF? */
375 return(0);
376 if (buf[0] == 'n' || buf[0] == 'N') {
377 fclose(in);
378 return(0);
379 }
380 if (buf[0] == 'y' || buf[0] == 'Y') {
381 fclose(in);
382 return(1);
383 }
384 fprintf(stderr,"please answer y/n.\n");
385 }
386 }
387 #endif /* MDEBUG */
388