campo-sirio/arch/_new.cpp

368 lines
12 KiB
C++
Executable File

//
// _NEW.CPP
//
// Source file for ArchiveLib 1.0
//
// Copyright (c) Greenleaf Software, Inc. 1994
// 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.
//
// REVISION HISTORY
//
// May 22, 1994 1.0A : First release
//
//
#include "arclib.h"
#pragma hdrstop
#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_GUI ) && !defined( AL_FLAT_MODEL )
#include <toolhelp.h>
#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
//
//
// This is the Microsoft MS-DOS version. It also looks like it is set
// up to work with Win 32s, but I'm not sure why, since we haven't
// released support for it yet. I think that is a typo.
//
// 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_MEMORY ) || defined( AL_FLAT_MODEL ) )
int PointerInHeap( void *p )
{
AL_ASSERT( _heapchk() == _HEAPOK, "Heap fails internal consistency check" );
_HEAPINFO heapinfo;
heapinfo._pentry = 0;
while ( _heapwalk( &heapinfo ) == _HEAPOK )
if ( heapinfo._pentry == (int __far *) p )
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_MEMORY ) || defined( AL_FLAT_MODEL ) )
int PointerInHeap( void *p )
{
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_MEMORY ) && defined( AL_LARGE_DATA )
int PointerInHeap( void *p )
{
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_MEMORY ) && !defined( AL_FLAT_MODEL ) && !defined( AL_LARGE_DATA ) && !defined( AL_WATCOM )
int PointerInHeap( void *p )
{
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 * ){ 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
//
void *operator new( size_t size )
{
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
//
void operator delete( void *ptr )
{
#if defined( AL_WINDOWS_MEMORY ) && !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 ];
char *ep = p + size;
AL_ASSERT( ( (long *) ep )[ 0 ] == 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