446 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			446 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| //
 | |
| // _NEW.CPP
 | |
| //
 | |
| //  Source file for ArchiveLib 2.0
 | |
| //
 | |
| //  Copyright (c) Greenleaf Software, Inc. 1994-1996
 | |
| //  All Rights Reserved
 | |
| //
 | |
| // CONTENTS
 | |
| //
 | |
| //  PointerInHeap()
 | |
| //  operator new()
 | |
| //  operator delete()
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  One of the defensive programming measures we have taken in this
 | |
| //  library is to provide versions of ::new and ::delete that do a
 | |
| //  little extra work.  Of course, these all go away if _DEBUG isn't
 | |
| //  defined.
 | |
| //
 | |
| //  Basically, there are a couple of things at work here.   First, we
 | |
| //  have taken over the new operator and delete operator, so we are
 | |
| //  guaranteed that all C++ memory allocation will take place through
 | |
| //  these routines.  Therefore, we can take certain liberties with
 | |
| //  them.
 | |
| //
 | |
| //  The most important thing we do when someone wants to allocate
 | |
| //  memory is to allocate an extra 12 bytes of data beyond what has
 | |
| //  been requested.  We use 8 bytes at the start of the block and
 | |
| //  4 bytes at the end of the block for our own purposes.  The pointer
 | |
| //  we return to the requester is actually at the start of the block
 | |
| //  plus eight bytes.
 | |
| //
 | |
| //  The first four bytes at the start of the block are used to store
 | |
| //  the size of the block.  The next four bytes hold a long word containing
 | |
| //  a leading picket, which is just a special word four bytes long.
 | |
| //  If the user underwrites the block of data for some reason, one of
 | |
| //  those four bytes will probably be corrupted.  At the end of the block,
 | |
| //  we store a trailing picket that has the same purpose.  It holds
 | |
| //  a special pattern of four bytes.  If the user overwrites the block
 | |
| //  of data, those four bytes will be corrupted.
 | |
| //
 | |
| //  We check the pickets when the ::delete operator is called.  That way,
 | |
| //  when an object is going to be freed, we can instantly detect if it
 | |
| //  has been abused in some fashion.
 | |
| //
 | |
| //  The ::delete operator here also attempts to make sure that the pointer
 | |
| //  being deleted points to a block that is actually in the heap.  This
 | |
| //  isn't always possible, but it works under most MS-DOS models, and
 | |
| //  works under Windows small and medium models.  Under Windows large
 | |
| //  memory models, we can walk the global heap to look for pointers,
 | |
| //  but we might not find them, since the RTL might be using a subsegment
 | |
| //  allocation scheme.
 | |
| //
 | |
| //  In addition to checking the heap, under Windows the ::delete function
 | |
| //  can also call the IsBadWritePtr() function to see if this is just
 | |
| //  a completely hosed up pointer.
 | |
| //
 | |
| //  Note that it is kind of obtrusive to redefine ::new and ::delete.  There
 | |
| //  is an excellent chance that this will interfere with other libraries,
 | |
| //  such as MFC.  Fortunately, we have made it easy to get around this.
 | |
| //  First, it is relatively simple to just delete this module from your
 | |
| //  library, using:  LIB ALXX-_NEW;  If you don't want to go to that
 | |
| //  trouble, you can also define AL_DISABLE_NEW and rebuild this module,
 | |
| //  which should also make it go away.  But if you don't need to make
 | |
| //  this code disappear, you ought to leave it in, it might save you a lot
 | |
| //  of trouble some day.
 | |
| //
 | |
| //  It's getting harder and harder to support this library, it interferes
 | |
| //  with things like MFC.  It may be removed from the library sometime soon.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //  May 22, 1994  1.0A    : First release
 | |
| //
 | |
| //  July 7, 1994  1.0B    : Had to make some minor adjustments in picket
 | |
| //                          management to work properly under UNIX.
 | |
| //
 | |
| //  January 9, 1995 1.01A : This stuff doesn't work *at all* when I am
 | |
| //                          am inside Borland's Power Pack DLLs.  This
 | |
| //                          code gets turned off in that case.
 | |
| //
 | |
| //  February 14, 1996 2.0A : New release w/no substantial changes
 | |
| //
 | |
| 
 | |
| #include "arclib.h"
 | |
| #if !defined( AL_IBM )
 | |
| #pragma hdrstop
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| //
 | |
| // The MS-DOS heapwalk functions are in different header files depending
 | |
| // on who you are.  Note that I don't walk the heap under MS-DOS with
 | |
| // Symantec or Watcom.  Not sure if I can.
 | |
| //
 | |
| #if defined( AL_BORLAND )
 | |
| #include <alloc.h>
 | |
| #elif defined( AL_MICROSOFT )
 | |
| #include <malloc.h>
 | |
| #endif
 | |
| //
 | |
| // Walking the Windows heap requires TOOLHELP.DLL.  It would be great
 | |
| // if Borland provided the TOOLHELP API under their DPMI extenders, but
 | |
| // I don't think they do.  I don't think the heap walk functions are
 | |
| // available under Win 32s either.
 | |
| //
 | |
| #if defined( AL_WINDOWS ) && !defined( AL_FLAT_MODEL )
 | |
| #include <toolhelp.h>
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // If I am using 16 bit Powerpack in a DLL, this stuff doesn't work.  I
 | |
| // detect it and disable it.
 | |
| //
 | |
| #if defined( AL_BORLAND ) && defined( __DPMI16__ )
 | |
| #ifndef AL_DISABLE_NEW
 | |
| #define AL_DISABLE_NEW
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // I don't think this will work when I am using the DLL version of
 | |
| // the Run Time Library under Borland
 | |
| //
 | |
| #if defined( AL_BORLAND ) && defined( _RTLDLL )
 | |
| #ifndef AL_DISABLE_NEW
 | |
| #define AL_DISABLE_NEW
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // Microsoft VC++ 4.0 doesn't work properly either...
 | |
| //
 | |
| #if defined( AL_MICROSOFT ) && (_MSC_VER >= 1000 )
 | |
| #ifndef AL_DISABLE_NEW
 | |
| #define AL_DISABLE_NEW
 | |
| #endif
 | |
| #endif
 | |
| /*
 | |
|  * To completely eliminate this stuff, all you have to do is define
 | |
|  * AL_DISABLE_NEW before rebuilding the library.
 | |
|  */
 | |
| #ifndef AL_DISABLE_NEW
 | |
| //
 | |
| // When I pop up an error message, it sometimes helps to know where it came
 | |
| // from.  This definition is used to create the message box.
 | |
| //
 | |
| #if defined( AL_BUILDING_DLL )
 | |
| #define LIB_TYPE "DLL"
 | |
| #else
 | |
| #define LIB_TYPE "Static"
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // If Debug is not turned on, none of this stuff happens.  I also don't
 | |
| // work with Microsoft huge model, things get nasty in there.
 | |
| //
 | |
| #if defined( _DEBUG ) && !( defined( AL_MICROSOFT ) && defined( _M_I86HM ) )
 | |
| 
 | |
| //
 | |
| // int PointerInHeap( void *p )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  p  : The pointer under test.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  An integer, true or false.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is called by ::delete() to see if the pointer we are
 | |
| //  trying to delete is in fact in the heap. If it isn't, we could cause
 | |
| //  quite a bit of trouble if we try to delete it.
 | |
| //
 | |
| //  Under MS-DOS, this function just executes the normal heapwalk functions
 | |
| //  supported by Microsoft and Borland.  Under Windows small memory
 | |
| //  models, we use the Toohelp API to walk the local heap.  Under all
 | |
| //  other circumstances, we just give up and always return a true value.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 22, 1994  1.0A  : First release
 | |
| //
 | |
| //   February 14, 1996 2.0A : New release
 | |
| //
 | |
| 
 | |
| //
 | |
| // This is the Microsoft MS-DOS version.  It seems to also work
 | |
| // with Win32s, and Windows NT Console.  Very confusing,
 | |
| // I hope the #ifdefs all work out!
 | |
| //
 | |
| // This function just uses the heapwalk RTL function to check for the
 | |
| // presence of the pointer in the heap.
 | |
| //
 | |
| #if defined( AL_MICROSOFT ) &&                                     \
 | |
|     ( !defined( AL_WINDOWS ) || defined( AL_FLAT_MODEL ) )
 | |
| 
 | |
| int
 | |
| PointerInHeap( void *p )  /* Tag debug function */
 | |
| {
 | |
|     AL_ASSERT( _heapchk() == _HEAPOK, "Heap fails internal consistency check" );
 | |
|     _HEAPINFO heapinfo;
 | |
|     heapinfo._pentry = 0;
 | |
|     while ( _heapwalk( &heapinfo ) == _HEAPOK )
 | |
| #if defined( AL_FLAT_MODEL )
 | |
|         if ( heapinfo._pentry == (int *) p )
 | |
| #else
 | |
|         if ( heapinfo._pentry == (int __far *) p )
 | |
| #endif
 | |
|             return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // This is the Borland MS-DOS version.  It looks like it also works under
 | |
| // Win 32s, which might be more reasonable, since we do support Borland
 | |
| // in that mode. 
 | |
| //
 | |
| // Like the previous function, this guy just uses the heapwalk API to
 | |
| // check the local heap for the presence of the pointer.
 | |
| //
 | |
| #elif defined( AL_BORLAND ) && ( !defined( AL_WINDOWS ) || defined( AL_FLAT_MODEL ) )
 | |
| 
 | |
| int
 | |
| PointerInHeap( void *p )  /* Tag debug function */
 | |
| {
 | |
|     AL_ASSERT( heapcheck() == _HEAPOK,
 | |
|                LIB_TYPE " heap fails internal consistency check" );
 | |
|     struct heapinfo info;
 | |
|     info.ptr = 0;
 | |
|     while ( heapwalk( &info ) == _HEAPOK )
 | |
| #if defined( AL_LARGE_DATA ) && !defined( AL_FLAT_MODEL )
 | |
|         if ( info.ptr == (void huge *) p )
 | |
|             return 1;
 | |
| #else
 | |
|         if ( info.ptr == p )
 | |
|             return 1;
 | |
| #endif
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Microsoft is nice enough to support the heapwalk API under Windows
 | |
| // large memory models also.  This is good, since the TOOLHELP API would
 | |
| // flounder when confronted with a subsegment allocation strategy.
 | |
| //
 | |
| #elif defined( AL_MICROSOFT ) && defined( AL_WINDOWS ) && defined( AL_LARGE_DATA )
 | |
| 
 | |
| int
 | |
| PointerInHeap( void *p )  /* Tag debug function */
 | |
| {
 | |
|     AL_ASSERT( _fheapchk() == _HEAPOK,
 | |
|                LIB_TYPE " heap fails internal consistency check" );
 | |
|     _HEAPINFO heapinfo;
 | |
|     heapinfo._pentry = 0;
 | |
|     while ( _fheapwalk( &heapinfo ) == _HEAPOK )
 | |
|         if ( heapinfo._pentry == p )
 | |
|             return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Under Windows small and medium memory models, the TOOLHELP API lets
 | |
| // us walk the local heap, looking for an entry.  No subsegment allocation
 | |
| // scheme will get in the way.
 | |
| //
 | |
| // I should be able to use this with Watcom, but I am using a little bit of
 | |
| // inline assembly to get my data segment.  This inline assembly won't
 | |
| // work with Watcom, so someday I will have to add a little code to
 | |
| // get things working right with them also.
 | |
| //
 | |
| #elif defined( AL_WINDOWS ) && !defined( AL_FLAT_MODEL ) && !defined( AL_LARGE_DATA ) && !defined( AL_WATCOM )
 | |
| 
 | |
| int
 | |
| PointerInHeap( void *p )  /* Tag debug function */
 | |
| {
 | |
|     LOCALENTRY LEntry;
 | |
|     WORD wHeap;
 | |
| //
 | |
| // I need to search the local heap that is in my data segment.
 | |
| //
 | |
|     _asm mov ax,ds
 | |
|     _asm mov wHeap,ax
 | |
|     LEntry.dwSize = sizeof( LOCALENTRY );
 | |
|     if ( LocalFirst( &LEntry, (HGLOBAL) wHeap ) ) {
 | |
|         do {
 | |
|             if ( LEntry.wAddress == (WORD) p )
 | |
|                 return 1;
 | |
|         } while ( LocalNext( &LEntry ) );
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // When all else fails, give up!
 | |
| //
 | |
| #else
 | |
| int
 | |
| PointerInHeap( void * )  /* Tag debug function */
 | |
| {
 | |
|     return 1;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // void *operator new( size_t size )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  size  : The amount of memory being requested.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A pointer to the newly allocated storage area, or a 0 in the event
 | |
| //  of failure.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This version of ::new() does what I described at the top of the file.
 | |
| //  It allocates a block of memory as requested, and includes eight
 | |
| //  extra bytes.  Four bytes are reserved at the start and end of the memory
 | |
| //  block for our "pickets".  These pickets hold a fixed pattern in memory
 | |
| //  that can be tested for accidental modification.  When ::delete() is 
 | |
| //  called, we check the area to see if the caller munged it, and
 | |
| //  cause an assertion error if they did.  The other four bytes are needed
 | |
| //  to keep the size of the block on hand.  Otherwise I wouldn't know how
 | |
| //  to get to the end of the block to check the trailing picket.
 | |
| //
 | |
| //  Note that if you are using set_new_handler() or exceptions, this stuff
 | |
| //  is probably going to hose you up badly.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 22, 1994  1.0A  : First release
 | |
| //
 | |
| //   July 7, 1994  1.0B  : On Sun UNIX, if I tried to write a long out to
 | |
| //                         a misaligned address, I get a fault.  The leading
 | |
| //                         pickets will always be properly aligned, but the
 | |
| //                         trailing picket could land anywhere.  So, under
 | |
| //                         UNIX, I store the trailing picket a byte at a time.
 | |
| //
 | |
| //   Februray 14, 1996  2.0A : New release
 | |
| //
 | |
| void *
 | |
| operator new( size_t size )  /* Tag debug function */
 | |
| {
 | |
|     if ( ( (long) size + 12 ) > 65535L )
 | |
|         return 0;
 | |
|     char *p = (char *) malloc( size + 12 );
 | |
|     if ( !p )
 | |
|         return 0;
 | |
|     ( (long *) p)[ 0 ] = (long) size;
 | |
|     ( (long *) p)[ 1 ] = 0x12345678L;
 | |
|     ( (long *)(p + 8 + size))[ 0 ] = 0xfedcba98L;
 | |
|     return p + 8;
 | |
| }
 | |
| 
 | |
| //
 | |
| // void operator delete( void *ptr )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  ptr :  A pointer to the memory block the user wishes to delete.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  After the user has had the chance to muck with this memory block for
 | |
| //  a while, he or she will want to return it to the heap.  We do a bunch
 | |
| //  of checks here before that happens, to see if any serious mistakes have
 | |
| //  been made.  If we detect any serious mistakes, we just abort the
 | |
| //  program with an assertion error.
 | |
| //
 | |
| //  First we check to see if Windows thinks it is even a valid pointer.  If
 | |
| //  we don't do this, some of the other code here will GPF if you call delete
 | |
| //  with a really bad pointer.  Those GPFs are a lot less informative than
 | |
| //  our nice assertion failures.
 | |
| // 
 | |
| //  If it looks like it is a valid pointer, the next thing we do is try to
 | |
| //  see if the pointer is in our heap.  A common mistake is trying to free
 | |
| //  a pointer twice, or freeing a pointer that has been incremented or
 | |
| //  decremented.  Either of these can royally foul the heap.
 | |
| //
 | |
| //  If it looks like the pointer really is in the heap, there is still one
 | |
| //  last thing to check.  I take a quick glance at both the leading and
 | |
| //  trailing pickets to see if either of them have been mangled.  A simple
 | |
| //  overwrite or underwrite by just one byte can be catastrophic, but we
 | |
| //  detect it easily here.
 | |
| //
 | |
| //  If all of that goes as expected, we are free to finally return the
 | |
| //  storage to the heap.  Just for good luck, I clear it out first.  That
 | |
| //  way if anyone is foolish enough to try and use the data after it has
 | |
| //  been deleted, they will at least see that there is nothing intelligent
 | |
| //  store there.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 22, 1994  1.0A  : First release
 | |
| //
 | |
| //   July 7, 1994  1.0B  : On Sun UNIX, if I tried to read a long from
 | |
| //                         a misaligned address, I get a fault.  The leading
 | |
| //                         pickets will always be properly aligned, but the
 | |
| //                         trailing picket could land anywhere.  So, under
 | |
| //                         UNIX, I check the trailing picket a byte at a time.
 | |
| //
 | |
| //   February 14, 1996 2.0A : New release.
 | |
| 
 | |
| void
 | |
| operator delete( void *ptr )  /* Tag debug function */
 | |
| {
 | |
| #if defined( AL_WINDOWS ) && !defined( AL_FLAT_MODEL )
 | |
|     AL_ASSERT( !IsBadWritePtr( ptr, 1 ), "delete: delete called for ptr Windows doesn't like" );
 | |
| #endif
 | |
|     char *p = (char *) ptr;
 | |
|     AL_ASSERT( PointerInHeap( p - 8 ),
 | |
|                "delete: delete called for pointer not found in the " LIB_TYPE " heap" );
 | |
|     AL_ASSERT( ( (long *) p )[ -1 ] == 0x12345678L,
 | |
|                 "delete : Data corrupted in object's leading picket in the " LIB_TYPE " heap"  );
 | |
|     size_t size = (size_t) ( (long *) p )[ -2 ];
 | |
| #if defined AL_UNIX
 | |
|     long picket = (unsigned char) p[ size ] << 24;
 | |
|     picket |= (unsigned char) p[ size + 1 ] << 16;
 | |
|     picket |= (unsigned char) p[ size + 2 ] << 8;
 | |
|     picket |= (unsigned char) p[ size + 3 ];
 | |
| #else
 | |
|     long picket = ( (long *)( p + size ) )[ 0 ];
 | |
| #endif
 | |
|     AL_ASSERT( picket == 0xfedcba98L,
 | |
|                 "delete : Data corrupted in object's trailing picket in the " LIB_TYPE " heap" );
 | |
|     memset( p - 8, size + 12, 0 ); //Clear it before freeing it
 | |
|     free( ((char *) p - 8 ) );
 | |
| }
 | |
| 
 | |
| #endif //#ifdef _DEBUG etc.
 | |
| 
 | |
| #endif // #ifdef AL_DISABLE_NEW
 | |
| 
 |