agc@nixbln.UUCP (09/19/88)
Posting-number: Volume 4, Issue 79 Submitted-by: "A. Nonymous" <agc@nixbln.UUCP> Archive-name: smap ["careware"?! ++bsa] Please find enclosed a package which I have called smap, for "Safe Memory Allocator Package". It acts as a wrapper around malloc, calloc, realloc and free to check that they behave themselves. The only thing I am not happy with is the name, so change it if you want. I have made this package into what I call "careware" - if people find it useful, I suggest that they send what they think it's worth to a charity of their choice. Regards, Alistair G. Crooks (...!uunet!linus!nixbur!nixpbe!nixbln!agc) #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./README` then echo "writing ./README" cat > ./README << '\Rogue\Monster\' smap - a safe memory allocator package. This package acts as a buffer around any calls to malloc, calloc, realloc and free, checking that everything takes place in an orderly manner. It is intended that this package should be used in the debug phase of a project. There are two main files contained herein - smap.h should be included in EVERY source file that calls any of malloc, calloc, realloc, or free. It should be included before the calls of these functions. It redefines the allocation and freeing routines to those included in the file smap.c smap.c contains "replacements", or outer shells, for malloc, calloc, realloc and free, and defines three new functions called _blkstart(), _blkend(), and _blkignore(). These functions introduce a new concept of a program allocation block. Each block is delimited by a _blkstart() and a _blkend(). When _blkend() is called, all memory that has been allocated since the last call of _blkstart() will be checked to see that it has been subsequently freed. Any memory purposely left allocated can be marked as such by calling _blkignore(ptr), where ptr is the pointer to the memory that was allocated. Allocation blocks can be nested. Possible errors which will be picked up are: _malloc: run out of slots * Re-compile smap.c with a larger MAXSLOTS * _calloc: run out of slots * Re-compile smap.c with a larger MAXSLOTS * _realloc: realloc not previously malloc'd _free: free not previously malloc'd _free: free after previous freeing Each of these errors, when encountered, will send the appropriate error message to the standard error stream, and then send a SIGQUIT signal to itself, thereby generating a core dump, for future information via a debugger. Possible warnings which will be picked up are: _malloc: unusual size %d bytes _malloc: unable to malloc %d bytes _malloc: malloc returned a non-freed pointer _calloc: unusual size %d bytes _calloc: unable to malloc %d bytes _calloc: malloc returned a non-freed pointer _realloc: realloc failure %d bytes _realloc: realloc after previous freeing _blkend: %d bytes unfreed _blkignore: pointer has not been allocated Upon detection of a warning condition, the appropriate error message will be sent to the standard error stream, and execution will continue. To install the package: 1. Place the file smap.h in a directory where it can be found by the compiler, if necessary adding the directory name to the list of include directories given to the compiler. 2. Place a C pre-processor call to '#include "smap.h"' in EVERY source file which calls any of malloc, calloc, realloc or free. This must be done BEFORE any calls to any of these functions. If either of these two conditions is not fulfilled, spurious errors will occur with this software, usually in the _free function. 3. Compile the file smap.c. 4. Re-compile all source files that have changed, and link your object files with the file smap.o. 5. Run the program, and investigate any core dumps which occur. ..... 6. When memory allocation bugs have been found, the package can be disabled by defining the preprocessor flag NOMEMCHECK, and recompiling all source modules that have included "smap.h". Note that the _blk*() routines will cease to work when you do this. This package was originally developed to locate instances of freeing memory twice, which was causing unusual core dumps in places not related to the problem. This package is intended to locate these bugs when they happen. I have used it both at home and at work, finding it very useful in my own work, and almost invaluable when integrating my work with other pieces, especially when written by other people. I have designated it "careware". If you find it useful, I suggest that you send what you think it is worth to a charity of your choice. I would also like you to give this package any distribution that you think it deserves. Alistair G. Crooks, Joypace Ltd., 2 Vale Road, Hawkhurst, Kent TN18 4BU, UK. (+44 5805 3114) UUCP Europe: ...!mcvax!unido!nixpbe!nixbln!agc UUCP the rest of the world: ...!uunet!linus!nixbur!nixpbe!nixbln!agc \Rogue\Monster\ else echo "will not over write ./README" fi if `test ! -s ./Makefile` then echo "writing ./Makefile" cat > ./Makefile << '\Rogue\Monster\' CFLAGS = -gx CC = mcc OBJ = smap.o SRC = smap.c INC = smap.h all : ${OBJ} tst ${OBJ} : ${INC} ${SRC} ${CC} ${CFLAGS} -c ${SRC} tst : tst.c ${INC} ${OBJ} ${CC} ${CFLAGS} tst.c ${OBJ} -o tst \Rogue\Monster\ else echo "will not over write ./Makefile" fi if `test ! -s ./smap.c` then echo "writing ./smap.c" cat > ./smap.c << '\Rogue\Monster\' /* * @(#)smap.c 1.2 30/08/88 16:28:19 agc * * Copyright 1988, Joypace Ltd., UK. This product is "careware". * If you find it useful, I suggest that you send what you think * it is worth to the charity of your choice. * * Alistair G. Crooks, +44 5805 3114 * Joypace Ltd., * 2 Vale Road, * Hawkhurst, * Kent TN18 4BU, * UK. * * UUCP Europe ...!mcvax!unido!nixpbe!nixbln!agc * UUCP everywhere else ...!uunet!linus!nixbur!nixpbe!nixbln!agc * * smap.c - source file for debugging aids. */ #ifndef lint char *nsccsid = "@(#)smap.c 1.2 30/08/88 16:28:19 agc"; #endif /* lint */ #include <stdio.h> #include <signal.h> typedef struct _slotstr { char *s_ptr; /* the allocated area */ unsigned int s_size; /* its size */ char s_freed; /* whether it's been freed yet */ char s_blkno; /* program block reference number */ } SLOT; #ifndef MAXSLOTS #define MAXSLOTS 4096 #endif /* MAXSLOTS */ static SLOT slots[MAXSLOTS]; static int slotc; static int blkno; #define WARNING(s1, s2) (void) fprintf(stderr, s1, s2) extern char *malloc(); extern char *calloc(); extern char *realloc(); /* * abort - print a warning on stderr, and send a SIGQUIT to ourself */ static void abort(s1, s2) char *s1; char *s2; { WARNING(s1, s2); (void) kill(getpid(), SIGQUIT); /* core dump here */ } /* * _malloc - wrapper around malloc. Warns if unusual size given, or the * real malloc returns a NULL pointer. Returns a pointer to the * malloc'd area */ char * _malloc(size) unsigned int size; { SLOT *sp; char *ptr; int i; if (size == 0) WARNING("_malloc: unusual size %d bytes\n", size); if ((ptr = (char *) malloc(size)) == (char *) NULL) WARNING("_malloc: unable to malloc %d bytes\n", size); for (i = 0, sp = slots ; i < slotc ; i++, sp++) if (sp->s_ptr == ptr) break; if (i == slotc) { if (slotc == MAXSLOTS - 1) abort("_malloc: run out of slots\n", (char *) NULL); sp = &slots[slotc++]; } else if (!sp->s_freed) WARNING("_malloc: malloc returned a non-freed pointer\n", NULL); sp->s_size = size; sp->s_freed = 0; sp->s_ptr = ptr; sp->s_blkno = blkno; return(sp->s_ptr); } /* * _calloc - wrapper for calloc. Calls _malloc to allocate the area, and * then sets the contents of the area to NUL bytes. Returns its address. */ char * _calloc(nel, size) int nel; unsigned int size; { unsigned int tot; char *ptr; char *cp; tot = nel * size; ptr = _malloc(tot); if ((cp = ptr) == (char *) NULL) return((char *) NULL); while (tot--) *cp++ = 0; return(ptr); } /* * _realloc - wrapper for realloc. Checks area already alloc'd and * not freed. Returns its address */ char * _realloc(ptr, size) char *ptr; unsigned int size; { SLOT *sp; int i; for (i = 0, sp = slots ; i < slotc ; i++, sp++) if (sp->s_ptr == ptr) break; if (i == slotc) abort("_realloc: realloc on unallocated area\n", (char *) NULL); if (sp->s_freed) WARNING("_realloc: realloc on freed area\n", (char *) NULL); if ((sp->s_ptr = (char *) realloc(ptr, size)) == (char *) NULL) WARNING("_realloc: realloc failure %d bytes\n", size); sp->s_size = size; sp->s_blkno = blkno; return(sp->s_ptr); } /* * _free - wrapper for free. Loop through allocated slots, until you * find the one corresponding to pointer. If none, then it's an attempt * to free an unallocated area. If it's already freed, then tell user. */ void _free(ptr) char *ptr; { SLOT *sp; int i; for (i = 0, sp = slots ; i < slotc ; i++, sp++) if (sp->s_ptr == ptr) break; if (i == slotc) abort("_free: free not previously malloc'd\n", (char *) NULL); if (sp->s_freed) abort("_free: free after previous freeing\n", (char *) NULL); (void) free(sp->s_ptr); sp->s_freed = 1; } /* * _blkstart - start of a program block. Increase the block reference * number by one. */ void _blkstart() { blkno += 1; } /* * _blkend - end of a program block. Check all areas allocated in this * block have been freed. Decrease the block number by one. */ void _blkend() { SLOT *sp; int i; if (blkno == 0) { WARNING("_blkend: unmatched call to _blkend\n", NULL); return; } for (i = 0, sp = slots ; i < slotc ; i++, sp++) if (sp->s_blkno == blkno && !sp->s_freed) WARNING("_blkend: %d bytes unfreed\n", sp->s_size); blkno -= 1; } /* * _blkignore - find the slot corresponding to ptr, and set its block * number to zero, to avoid _blkend picking it up when checking. */ void _blkignore(ptr) char *ptr; { SLOT *sp; int i; for (i = 0, sp = slots ; i < slotc ; i++, sp++) if (sp->s_ptr == ptr) break; if (i == slotc) WARNING("_blkignore: pointer has not been allocated\n", NULL); else sp->s_blkno = 0; } \Rogue\Monster\ else echo "will not over write ./smap.c" fi if `test ! -s ./smap.h` then echo "writing ./smap.h" cat > ./smap.h << '\Rogue\Monster\' /* * @(#)smap.h 1.1 30/08/88 16:07:36 agc * * Copyright 1988, Joypace Ltd., UK. This product is "careware". * If you find it useful, I suggest that you send what you think * it is worth to the charity of your choice. * * Alistair G. Crooks, +44 5805 3114 * Joypace Ltd., * 2 Vale Road, * Hawkhurst, * Kent TN18 4BU, * UK. * * UUCP Europe ...!mcvax!unido!nixpbe!nixbln!agc * UUCP everywhere else ...!uunet!linus!nixbur!nixpbe!nixbln!agc * * smap.h - include file for debugging aids. This file must be included, * before any calls, in any source file that calls malloc, calloc, * realloc, or free. (Note alloca is not included in this list). */ #ifdef NOMEMCHECK #define _blkstart() #define _blkend() #define _blkignore(p) #else /* not NOMEMCHECK */ #ifndef malloc #define malloc _malloc #define calloc _calloc #define realloc _realloc #define free _free #endif /* not malloc */ extern void _blkstart(); extern void _blkend(); extern void _blkignore(); #endif /* not NOMEMCHECK */ \Rogue\Monster\ else echo "will not over write ./smap.h" fi if `test ! -s ./tst.c` then echo "writing ./tst.c" cat > ./tst.c << '\Rogue\Monster\' #include <stdio.h> #include "smap.h" main() { char *ptr; char *ign; _blkstart(); _blkstart(); if ((ptr = (char *) malloc(10)) == (char *) NULL) (void) fprintf(stderr, "malloc failure\n"); (void) free(ptr); _blkend(); if ((ptr = (char *) malloc(10)) == (char *) NULL) (void) fprintf(stderr, "malloc failure\n"); if ((ign = (char *) malloc(20)) == (char *) NULL) (void) fprintf(stderr, "malloc failure\n"); _blkignore(ign); (void) free(ptr); _blkend(); _blkend(); (void) free(ptr); } \Rogue\Monster\ else echo "will not over write ./tst.c" fi echo "Finished archive 1 of 1" exit