Pointer validation code

donl mathis donl at glass.esd.sgi.com
Wed Feb 6 11:57:40 AEST 1991


In article <9102041731.aa11984 at WOLF.BRL.MIL>, mike at BRL.MIL (Mike Muuss) writes:
> I handle memory corruption and pointer mis-handling in a rather
> different manner.  I have "wrapper" subroutines called
> rt_malloc(), rt_free(), rt_calloc() that take an extra string
> argument which indicates the "purpose" of this allocation/free.
> 
> Depending on the setting of some global variables, you can
> independently enable memory activity logging, and memory
> checking.  The checking includes adding a "barrier" word at the
> end of the allocation, to ensure that the application is not
> running off the end.  It also maintains a full table of what memory
> is being used.  This table can be printed at any time, by calling
> a subroutine (either from the application, or via a "dbx -P").
> 
> One final note is that most of our application's data structures have
> been designed with "magic numbers" as their first word, so that
> subroutines can validate that they have been given pointers to the
> correct kind of object.
> 
> These two techniques have been very powerful, and have virtually eliminated
> the programming difficulties associated with using very complex
> dynamic memory allocation in C.

I have a similar package that accomplish much the same thing, but in a
somewhat more formal environment.  My model for memory use was that a
program will enter a logical scope where it will be building a new
structure of some kind, allocate many bits and pieces within the
structure, and then throw the whole thing away at once.  Any number of
such scopes may coexist.  I abandoned the notion of explicitly freeing
individual values, and at least in my applications, have not missed
it.

A scope is created by opening a "memory group", which has a name and
perhaps some tuning parameters.  A pointer to struct is returned to the
caller, and all allocation requests are made against that handled.
Relatively large blocks of memory are added to the group as necessary.
Each block has a header that contains a pointer to the current free
space within the block.  Individual allocation requests are generally
satisfied by simply keeping an eye on the free count, and bumping the
pointer into the block.  "Freeing" the group consists of resetting the
block pointers, also quite fast.  A group can be destroyed and its
blocks returned to the pool.

Verification gets easier once you have a structure describing the list
of blocks, and block headers that describe the memory that is part of
the group.  It is a simple matter to verify that a pointer to an object
of a known size is completely within a block in the group, and that a
group appears to be self-consistent.  (One of the routines in my test
suite repeatedly fires random bytes into memory and calls the
verification routines; it usually, though not always, notices a problem
before the program dumps core.)

On top of these basic mechanisms, there is a user interface layer
designed to make memory easy to get and use.  Macros provided typed
allocation of the appropriate size, and there is an extension to the
basic mechanism for variable-sized arrays that grow as necessary when a
reference goes out of the current bounds.  Macros also providing
scaling by N to allocate arrays without a fuss.

I do not know how well this mechanism would fit into C++, nor do i know
how much of a pain it would be to adapt it to multiprocessing.  The
package as it sits is about 1300 lines of C.  My last tests indicated
a significant performance increase over malloc/free, although they were
done some time ago, and i don't know what changes have occured since
then.
--

- donl mathis at Silicon Graphics Computer Systems, Mountain View, CA

donl at sgi.com

There is One.



More information about the Comp.sys.sgi mailing list