]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/smartall.c
Initial revision
[bacula/bacula] / bacula / src / lib / smartall.c
1 /*
2
3                          S M A R T A L L O C
4                         Smart Memory Allocator
5
6         Evolved   over   several  years,  starting  with  the  initial
7         SMARTALLOC code for AutoSketch in 1986, guided  by  the  Blind
8         Watchbreaker,  John  Walker.  Isolated in this general-purpose
9         form in  September  of  1989.   Updated  with  be  more  POSIX
10         compliant  and  to  include Web-friendly HTML documentation in
11         October  of  1998  by  the  same  culprit.    For   additional
12         information and the current version visit the Web page:
13
14                   http://www.fourmilab.ch/smartall/
15
16 */
17
18 /*
19    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
20
21    This program is free software; you can redistribute it and/or
22    modify it under the terms of the GNU General Public License as
23    published by the Free Software Foundation; either version 2 of
24    the License, or (at your option) any later version.
25
26    This program is distributed in the hope that it will be useful,
27    but WITHOUT ANY WARRANTY; without even the implied warranty of
28    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29    General Public License for more details.
30
31    You should have received a copy of the GNU General Public
32    License along with this program; if not, write to the Free
33    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34    MA 02111-1307, USA.
35
36  */
37
38 #include "bacula.h"
39 /* Use the real routines here */
40 #undef realloc
41 #undef calloc
42 #undef malloc
43 #undef free
44       
45 #define FULL_SM_CHECK 1
46
47 /*LINTLIBRARY*/
48
49
50 #ifdef SMARTALLOC
51
52 extern char my_name[];                /* daemon name */
53
54 typedef unsigned short sm_ushort;
55
56 #define EOS      '\0'              /* End of string sentinel */
57 #define sm_min(a, b) ((a) < (b) ? (a) : (b))
58
59 /*  Queue data structures  */
60
61 /*  Memory allocation control structures and storage.  */
62
63 struct abufhead {
64         struct b_queue abq;          /* Links on allocated queue */
65         unsigned ablen;            /* Buffer length in bytes */
66         char *abfname;             /* File name pointer */
67         sm_ushort ablineno;        /* Line number of allocation */ 
68 };
69
70 static struct b_queue abqueue = {    /* Allocated buffer queue */
71         &abqueue, &abqueue
72 };
73
74 static Boolean bufimode = False;   /* Buffers not tracked when True */
75
76 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
77
78
79 /*  SMALLOC  --  Allocate buffer, enqueing on the orphaned buffer
80                  tracking list.  */
81
82 static void *smalloc(char *fname, int lineno, unsigned int nbytes)
83 {
84         char *buf;
85
86         /* Note:  Unix  MALLOC  actually  permits  a zero length to be
87            passed and allocates a valid block with  zero  user  bytes.
88            Such  a  block  can  later  be expanded with realloc().  We
89            disallow this based on the belief that it's better to  make
90            a  special case and allocate one byte in the rare case this
91            is desired than to miss all the erroneous occurrences where
92            buffer length calculation code results in a zero.  */
93
94         ASSERT(nbytes > 0);
95
96         nbytes += HEAD_SIZE + 1;
97         if ((buf = (char *) malloc(nbytes)) != NULL) {
98            /* Enqueue buffer on allocated list */
99            qinsert(&abqueue, (struct b_queue *) buf);
100            ((struct abufhead *) buf)->ablen = nbytes;
101            ((struct abufhead *) buf)->abfname = bufimode ? NULL : fname;
102            ((struct abufhead *) buf)->ablineno = (sm_ushort) lineno;
103            /* Emplace end-clobber detector at end of buffer */
104            buf[nbytes - 1] = (((long) buf) & 0xFF) ^ 0xC5;
105            buf += HEAD_SIZE;  /* Increment to user data start */
106         }
107 #ifdef FULL_SM_CHECK
108         sm_check(fname, lineno, True);
109 #endif
110         Dmsg4(1150, "smalloc %d at %x from %s:%d\n", nbytes, buf, fname, lineno);
111         return (void *) buf;
112 }
113
114 /*  SM_NEW_OWNER -- Update the File and line number for a buffer
115                     This is to accomodate mem_pool. */
116
117 void sm_new_owner(char *fname, int lineno, char *buf)
118 {
119         buf -= HEAD_SIZE;  /* Decrement to header */
120         ((struct abufhead *) buf)->abfname = bufimode ? NULL : fname;
121         ((struct abufhead *) buf)->ablineno = (sm_ushort) lineno;
122         return;
123 }
124
125 /*  SM_FREE  --  Update free pool availability.  FREE is never called
126                  except  through  this interface or by actuallyfree().
127                  free(x)  is  defined  to  generate  a  call  to  this
128                  routine.  */
129
130 void sm_free(char *file, int line, void *fp)
131 {
132         char *cp = (char *) fp;
133         struct b_queue *qp;
134
135 #ifdef FULL_SM_CHECK
136         sm_check(__FILE__, __LINE__, True);
137 #endif
138         if (cp == NULL) {
139            Emsg2(M_ABORT, 0, "Attempt to free NULL called from %s:%d\n", file, line);
140         }
141
142         cp -= HEAD_SIZE;
143         qp = (struct b_queue *) cp;
144
145         Dmsg4(1150, "sm_free %d at %x from %s:%d\n", 
146               ((struct abufhead *)cp)->ablen, fp, 
147               ((struct abufhead *)cp)->abfname, ((struct abufhead *)cp)->ablineno);
148
149         /* The following assertions will catch virtually every release
150            of an address which isn't an allocated buffer. */
151         if (qp->qnext->qprev != qp) {
152            Emsg2(M_ABORT, 0, "qp->qnext->qprev != qp called from %s:%d\n", file, line);
153         }
154         if (qp->qprev->qnext != qp) {
155            Emsg2(M_ABORT, 0, "qp->qprev->qnext != qp called from %s:%d\n", file, line);
156         }
157
158         /* The following assertion detects storing off the  end  of  the
159            allocated  space in the buffer by comparing the end of buffer
160            checksum with the address of the buffer.  */
161
162         if (((unsigned char *) cp)[((struct abufhead *) cp)->ablen - 1] !=
163                  ((((long) cp) & 0xFF) ^ 0xC5)) {
164            Emsg2(M_ABORT, 0, "Buffer overrun called from %s:%d\n", file, line);
165         }
166
167
168         qdchain(qp);
169
170         /* Now we wipe the contents of  the  just-released  buffer  with
171            "designer  garbage"  (Duff  Kurland's  phrase) of alternating
172            bits.  This is intended to ruin the day for any miscreant who
173            attempts to access data through a pointer into storage that's
174            been previously released. */
175
176         memset(cp, 0xAA, (int) ((struct abufhead *) cp)->ablen);
177
178         free(cp);
179 }
180
181 /*  SM_MALLOC  --  Allocate buffer.  NULL is returned if no memory
182                    was available.  */
183
184 void *sm_malloc(char *fname, int lineno, unsigned int nbytes)
185 {
186         void *buf;
187
188         if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
189
190            /* To catch sloppy code that assumes  buffers  obtained  from
191               malloc()  are  zeroed,  we  preset  the buffer contents to
192               "designer garbage" consisting of alternating bits.  */
193
194            memset(buf, 0x55, (int) nbytes);
195         }
196         return buf;
197 }
198
199 /*  SM_CALLOC  --  Allocate an array and clear it to zero.  */
200
201 void *sm_calloc(char *fname, int lineno,
202                 unsigned int nelem, unsigned int elsize)
203 {
204         void *buf;
205
206         if ((buf = smalloc(fname, lineno, nelem * elsize)) != NULL) {
207            memset(buf, 0, (int) (nelem * elsize));
208         }
209         return buf;
210 }
211
212 /*  SM_REALLOC  --  Adjust the size of a  previously  allocated  buffer.
213                     Note  that  the trick of "resurrecting" a previously
214                     freed buffer with realloc() is NOT supported by this
215                     function.   Further, because of the need to maintain
216                     our control storage, SM_REALLOC must always allocate
217                     a  new  block  and  copy  the data in the old block.
218                     This may result in programs which make heavy use  of
219                     realloc() running much slower than normally.  */
220
221 void *sm_realloc(char *fname, int lineno, void *ptr, unsigned int size)
222 {
223         unsigned osize;
224         void *buf;
225         char *cp = (char *) ptr;
226
227 #ifdef FULL_SM_CHECK
228         sm_check(fname, lineno, True);
229 #endif
230         if (size <= 0) {
231            e_msg(fname, lineno, M_ABORT, 0, "sm_realloc size: %d\n", size);
232         }
233
234         /*  If  the  old  block  pointer  is  NULL, treat realloc() as a
235            malloc().  SVID is silent  on  this,  but  many  C  libraries
236            permit this.  */
237
238         if (ptr == NULL)
239            return sm_malloc(fname, lineno, size);
240
241         /* If the old and new sizes are the same, be a nice guy and just
242            return the buffer passed in.  */
243
244         cp -= HEAD_SIZE;
245         osize = ((struct abufhead *) cp)->ablen -
246                 (HEAD_SIZE + 1);
247         if (size == osize) {
248            return ptr;
249         }
250
251         /* Sizes differ.  Allocate a new buffer of the  requested  size.
252            If  we  can't  obtain  such a buffer, act as defined in SVID:
253            return NULL from  realloc()  and  leave  the  buffer  in  PTR
254            intact.  */
255
256         if ((buf = smalloc(fname, lineno, size)) != NULL) {
257            memcpy(buf, ptr, (int) sm_min(size, osize));
258            /* If the new buffer is larger than the old, fill the balance
259               of it with "designer garbage". */
260            if (size > osize) {
261               memset(((char *) buf) + osize, 0x55, (int) (size - osize));
262            }
263
264            /* All done.  Free and dechain the original buffer. */
265
266            sm_free(__FILE__, __LINE__, ptr);
267         }
268         Dmsg4(150, "sm_realloc %d at %x from %s:%d\n", size, buf, fname, lineno);
269 #ifdef FULL_SM_CHECK
270         sm_check(fname, lineno, True);
271 #endif
272         return buf;
273 }
274
275 /*  ACTUALLYMALLOC  --  Call the system malloc() function to obtain
276                         storage which will eventually be released
277                         by system or library routines not compiled
278                         using SMARTALLOC.  */
279
280 void *actuallymalloc(unsigned int size)
281 {
282         return malloc(size);
283 }
284
285 /*  ACTUALLYCALLOC  --  Call the system calloc() function to obtain
286                         storage which will eventually be released
287                         by system or library routines not compiled
288                         using SMARTALLOC.  */
289
290 void *actuallycalloc(unsigned int nelem, unsigned int elsize)
291 {
292         return calloc(nelem, elsize);
293 }
294
295 /*  ACTUALLYREALLOC  --  Call the system realloc() function to obtain
296                          storage which will eventually be released
297                          by system or library routines not compiled
298                          using SMARTALLOC.  */
299
300 void *actuallyrealloc(void *ptr, unsigned int size)
301 {
302         return realloc(ptr, size);
303 }
304
305 /*  ACTUALLYFREE  --  Interface to system free() function to release
306                       buffers allocated by low-level routines. */
307
308 void actuallyfree(void *cp)
309 {
310         free(cp);
311 }
312
313 /*  SM_DUMP  --  Print orphaned buffers (and dump them if BUFDUMP is
314                  True). */
315
316 void sm_dump(Boolean bufdump)
317 {
318         struct abufhead *ap = (struct abufhead *) abqueue.qnext;
319
320         while (ap != (struct abufhead *) &abqueue) {
321
322            if ((ap == NULL) ||
323                (ap->abq.qnext->qprev != (struct b_queue *) ap) || 
324                (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
325               fprintf(stderr,
326                  "\nOrphaned buffers exist.  Dump terminated following\n");
327               fprintf(stderr,
328                  "  discovery of bad links in chain of orphaned buffers.\n");
329               fprintf(stderr,
330                  "  Buffer address with bad links: %lx\n", (long) ap);
331               break;
332            }
333
334            if (ap->abfname != NULL) {
335               unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
336               char errmsg[80];
337
338               sprintf(errmsg,
339                 "Orphaned buffer:  %6u bytes allocated at line %d of %s %s\n",
340                  memsize, ap->ablineno, my_name, ap->abfname
341               );
342               fprintf(stderr, "%s", errmsg);
343               if (bufdump) {
344                  unsigned llen = 0;
345                  char *cp = ((char *) ap) + HEAD_SIZE;
346
347                  errmsg[0] = EOS;
348                  while (memsize) {
349                     if (llen >= 16) {
350                        strcat(errmsg, "\n");
351                        llen = 0;
352                        fprintf(stderr, "%s", errmsg);
353                        errmsg[0] = EOS;
354                     }
355                     sprintf(errmsg + strlen(errmsg), " %02X",
356                        (*cp++) & 0xFF);
357                     llen++;
358                     memsize--;
359                  }
360                  fprintf(stderr, "%s\n", errmsg);
361               }
362            }
363            ap = (struct abufhead *) ap->abq.qnext;
364         }
365 }
366
367 /*  SM_CHECK --  Check the buffers and dump if any damage exists. */
368 void sm_check(char *fname, int lineno, Boolean bufdump)
369 {
370         if (!sm_check_rtn(fname, lineno, bufdump)) {
371            Emsg2(M_ABORT, 0, "Damaged buffer found. Called from %s:%d\n",
372               fname, lineno);
373         }
374 }
375
376 /*  SM_CHECK_RTN -- Check the buffers and return 1 if OK otherwise 0 */
377 int sm_check_rtn(char *fname, int lineno, Boolean bufdump)
378 {
379         struct abufhead *ap = (struct abufhead *) abqueue.qnext;
380         int bad, badbuf = 0;
381
382         while (ap != (struct abufhead *) &abqueue) {
383            bad = 0;
384            if ((ap == NULL) ||
385                (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
386               bad = 0x1;
387            }
388            if (ap->abq.qprev->qnext != (struct b_queue *) ap) { 
389               bad |= 0x2;
390            }
391            if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
392                 ((((long) ap) & 0xFF) ^ 0xC5)) {
393               bad |= 0x4;
394            }
395            badbuf |= bad;
396            if (bad) {
397               Emsg2(M_FATAL, 0, 
398                  "\nDamaged buffers found at %s:%d\n", fname, lineno);
399               if (bad & 0x1) {
400                  Emsg0(M_FATAL, 0, "  discovery of bad prev link.\n");
401               }
402               if (bad & 0x2) {
403                  Emsg0(M_FATAL, 0, "  discovery of bad next link.\n");
404               }
405               if (bad & 0x4) {
406                  Emsg0(M_FATAL, 0, "  discovery of data overrun.\n");
407               }
408
409               Emsg1(M_FATAL, 0, "  Buffer address: %lx\n", (long) ap);
410
411               if (ap->abfname != NULL) {
412                  unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
413                  char errmsg[80];
414
415                  sprintf(errmsg,
416                    "Damaged buffer:  %6u bytes allocated at line %d of %s %s\n",
417                     memsize, ap->ablineno, my_name, ap->abfname
418                  );
419                  Emsg1(M_FATAL, 0, "%s", errmsg);
420                  if (bufdump) {
421                     unsigned llen = 0;
422                     char *cp = ((char *) ap) + HEAD_SIZE;
423
424                     errmsg[0] = EOS;
425                     while (memsize) {
426                        if (llen >= 16) {
427                           strcat(errmsg, "\n");
428                           llen = 0;
429                           fprintf(stderr, "%s", errmsg);
430                           errmsg[0] = EOS;
431                        }
432                        if (*cp < 0x20) {
433                           sprintf(errmsg + strlen(errmsg), " %02X",
434                              (*cp++) & 0xFF);
435                        } else {
436                           sprintf(errmsg + strlen(errmsg), " %c ",
437                              (*cp++) & 0xFF);
438                        }
439                        llen++;
440                        memsize--;
441                     }
442                     Emsg1(M_FATAL, 0, "%s\n", errmsg);
443                  }
444               }
445            }
446            ap = (struct abufhead *) ap->abq.qnext;
447         }
448         return badbuf ? 0 : 1;
449 }
450
451
452 /*  SM_STATIC  --  Orphaned buffer detection can be disabled  (for  such
453                    items  as buffers allocated during initialisation) by
454                    calling   sm_static(1).    Normal   orphaned   buffer
455                    detection  can be re-enabled with sm_static(0).  Note
456                    that all the other safeguards still apply to  buffers
457                    allocated  when  sm_static(1)  mode is in effect.  */
458
459 void sm_static(int mode)
460 {
461         bufimode = (Boolean) (mode != 0);
462 }
463
464 #endif