v04i079: smap -- safe memory allocator package

agc at nixbln.UUCP agc at nixbln.UUCP
Mon Sep 19 11:45:27 AEST 1988


Posting-number: Volume 4, Issue 79
Submitted-by: "A. Nonymous" <agc at 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



More information about the Comp.sources.misc mailing list