From 985f330d3d71d104a531aefa676d06de3b4a16f0 Mon Sep 17 00:00:00 2001 From: rich_sposato Date: Thu, 8 Dec 2005 22:08:20 +0000 Subject: [PATCH] Added functions to check for corrupted Chunks and FixedAllocators. Made several minor coding changes. git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@377 7ec92016-0320-0410-acc4-a06ded1c099a --- src/SmallObj.cpp | 350 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 322 insertions(+), 28 deletions(-) diff --git a/src/SmallObj.cpp b/src/SmallObj.cpp index d05e7cd..698b523 100644 --- a/src/SmallObj.cpp +++ b/src/SmallObj.cpp @@ -20,12 +20,13 @@ #include #include +#include //#define DO_EXTRA_LOKI_TESTS +//#define USE_NEW_TO_ALLOCATE #ifdef DO_EXTRA_LOKI_TESTS #include - #include #endif @@ -108,11 +109,33 @@ namespace Loki /// Releases the allocated block of memory. void Release(); - bool IsCorrupt( unsigned char numBlocks, std::size_t blockSize ) const; + /** Determines if the Chunk has been corrupted. + @param numBlocks Total # of blocks in the Chunk. + @param blockSize # of bytes in each block. + @param checkIndexes True if caller wants to check indexes of available + blocks for corruption. If false, then caller wants to skip some + tests tests just to run faster. (Debug version does more checks, but + release version runs faster.) + @return True if Chunk is corrupt. + */ + bool IsCorrupt( unsigned char numBlocks, std::size_t blockSize, + bool checkIndexes ) const; + + /** Determines if block is available. + @param p Address of block managed by Chunk. + @param numBlocks Total # of blocks in the Chunk. + @param blockSize # of bytes in each block. + @return True if block is available, else false if allocated. + */ + bool IsBlockAvailable( void * p, unsigned char numBlocks, + std::size_t blockSize ) const; /// Returns true if block at address P is inside this Chunk. - inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const - { return ( pData_ <= p ) && ( p < pData_ + chunkLength ); } + inline bool HasBlock( void * p, std::size_t chunkLength ) const + { + unsigned char * pc = static_cast< unsigned char * >( p ); + return ( pData_ <= pc ) && ( pc < pData_ + chunkLength ); + } inline bool HasAvailable( unsigned char numBlocks ) const { return ( blocksAvailable_ == numBlocks ); } @@ -238,6 +261,14 @@ namespace Loki */ std::size_t CountEmptyChunks( void ) const; + /** Determines if FixedAllocator is corrupt. Checks data members to + see if any have erroneous values, or violate class invariants. It + also checks if any Chunk is corrupt. Complexity is O(C) where C is + the number of Chunks. If any data is corrupt, this will return true + in release mode, or assert in debug mode. + */ + bool IsCorrupt( void ) const; + /** Returns true if the block at address p is within a Chunk owned by this FixedAllocator. Complexity is O(C) where C is the total number of Chunks - empty or used. @@ -251,7 +282,6 @@ namespace Loki }; - // Chunk::Init ---------------------------------------------------------------- bool Chunk::Init( std::size_t blockSize, unsigned char blocks ) @@ -338,7 +368,7 @@ void Chunk::Deallocate(void* p, std::size_t blockSize) #if defined(DEBUG) || defined(_DEBUG) // Check if block was already deleted. Attempting to delete the same // block more than once causes Chunk's linked-list of stealth indexes to - // become corrupt. And causes count of blocksAvailable_ ) to be wrong. + // become corrupt. And causes count of blocksAvailable_ to be wrong. if ( 0 < blocksAvailable_ ) assert( firstAvailableBlock_ != index ); #endif @@ -353,38 +383,144 @@ void Chunk::Deallocate(void* p, std::size_t blockSize) // Chunk::IsCorrupt ----------------------------------------------------------- -bool Chunk::IsCorrupt( unsigned char numBlocks, std::size_t blockSize ) const +bool Chunk::IsCorrupt( unsigned char numBlocks, std::size_t blockSize, + bool checkIndexes ) const { - if ( numBlocks < blocksAvailable_ ) return true; - if ( 0 == blocksAvailable_ ) return false; + if ( numBlocks < blocksAvailable_ ) + { + // Contents at this Chunk corrupted. This might mean something has + // overwritten memory owned by the Chunks container. + assert( false ); + return true; + } + if ( IsFilled() ) + // Useless to do further corruption checks if all blocks allocated. + return false; unsigned char index = firstAvailableBlock_; - if ( numBlocks <= index ) return true; + if ( numBlocks <= index ) + { + // Contents at this Chunk corrupted. This might mean something has + // overwritten memory owned by the Chunks container. + assert( false ); + return true; + } + if ( !checkIndexes ) + // Caller chose to skip more complex corruption tests. + return false; -#ifdef DO_EXTRA_LOKI_TESTS - std::bitset< 256 > foundBlocks; + /* If the bit at index was set in foundBlocks, then the stealth index was + found on the linked-list. + */ + std::bitset< UCHAR_MAX > foundBlocks; unsigned char * nextBlock = NULL; - unsigned char cc = 0; - for ( ;; ) + /* The loop goes along singly linked-list of stealth indexes and makes sure + that each index is within bounds (0 <= index < numBlocks) and that the + index was not already found while traversing the linked-list. The linked- + list should have exactly blocksAvailable_ nodes, so the for loop will not + check more than blocksAvailable_. This loop can't check inside allocated + blocks for corruption since such blocks are not within the linked-list. + Contents of allocated blocks are not changed by Chunk. + + Here are the types of corrupted link-lists which can be verified. The + corrupt index is shown with asterisks in each example. + + Type 1: Index is too big. + numBlocks == 64 + blocksAvailable_ == 7 + firstAvailableBlock_ -> 17 -> 29 -> *101* + There should be no indexes which are equal to or larger than the total + number of blocks. Such an index would refer to a block beyond the + Chunk's allocated domain. + + Type 2: Index is repeated. + numBlocks == 64 + blocksAvailable_ == 5 + firstAvailableBlock_ -> 17 -> 29 -> 53 -> *17* -> 29 -> 53 ... + No index should be repeated within the linked-list since that would + indicate the presence of a loop in the linked-list. + */ + for ( unsigned char cc = 0; ; ) { nextBlock = pData_ + ( index * blockSize ); foundBlocks.set( index, true ); ++cc; if ( cc >= blocksAvailable_ ) + // Successfully counted off number of nodes in linked-list. break; index = *nextBlock; if ( numBlocks <= index ) + { + /* This catches Type 1 corruptions as shown in above comments. + This implies that a block was corrupted due to a stray pointer + or an operation on a nearby block overran the size of the block. + */ + assert( false ); return true; + } if ( foundBlocks.test( index ) ) + { + /* This catches Type 2 corruptions as shown in above comments. + This implies that a block was corrupted due to a stray pointer + or an operation on a nearby block overran the size of the block. + Or perhaps the program tried to delete a block more than once. + */ + assert( false ); return true; + } } if ( foundBlocks.count() != blocksAvailable_ ) + { + /* This implies that the singly-linked-list of stealth indexes was + corrupted. Ideally, this should have been detected within the loop. + */ + assert( false ); + return true; + } + + return false; +} + +// Chunk::IsBlockAvailable ---------------------------------------------------- + +bool Chunk::IsBlockAvailable( void * p, unsigned char numBlocks, + std::size_t blockSize ) const +{ + + if ( IsFilled() ) return false; -#else - (void)blockSize; // cast as void to make warning go away. -#endif + unsigned char * place = static_cast< unsigned char * >( p ); + // Alignment check + assert( ( place - pData_ ) % blockSize == 0 ); + unsigned char blockIndex = static_cast< unsigned char >( + ( place - pData_ ) / blockSize ); + + unsigned char index = firstAvailableBlock_; + assert( numBlocks > index ); + if ( index == blockIndex ) + return true; + + /* If the bit at index was set in foundBlocks, then the stealth index was + found on the linked-list. + */ + std::bitset< UCHAR_MAX > foundBlocks; + unsigned char * nextBlock = NULL; + for ( unsigned char cc = 0; ; ) + { + nextBlock = pData_ + ( index * blockSize ); + foundBlocks.set( index, true ); + ++cc; + if ( cc >= blocksAvailable_ ) + // Successfully counted off number of nodes in linked-list. + break; + index = *nextBlock; + if ( index == blockIndex ) + return true; + assert( numBlocks > index ); + assert( !foundBlocks.test( index ) ); + } return false; } @@ -444,16 +580,130 @@ std::size_t FixedAllocator::CountEmptyChunks( void ) const #endif } +// FixedAllocator::IsCorrupt -------------------------------------------------- + +bool FixedAllocator::IsCorrupt( void ) const +{ + const bool isEmpty = chunks_.empty(); + ChunkCIter start( chunks_.begin() ); + ChunkCIter last( chunks_.end() ); + const size_t emptyChunkCount = CountEmptyChunks(); + + if ( isEmpty ) + { + if ( start != last ) + { + assert( false ); + return true; + } + if ( 0 < emptyChunkCount ) + { + assert( false ); + return true; + } + if ( NULL != deallocChunk_ ) + { + assert( false ); + return true; + } + if ( NULL != allocChunk_ ) + { + assert( false ); + return true; + } + if ( NULL != emptyChunk_ ) + { + assert( false ); + return true; + } + } + + else + { + const Chunk * front = &chunks_.front(); + const Chunk * back = &chunks_.back(); + if ( start >= last ) + { + assert( false ); + return true; + } + if ( back < deallocChunk_ ) + { + assert( false ); + return true; + } + if ( back < allocChunk_ ) + { + assert( false ); + return true; + } + if ( front > deallocChunk_ ) + { + assert( false ); + return true; + } + if ( front > allocChunk_ ) + { + assert( false ); + return true; + } + + switch ( emptyChunkCount ) + { + case 0: + if ( emptyChunk_ != NULL ) + { + assert( false ); + return true; + } + break; + case 1: + if ( emptyChunk_ == NULL ) + { + assert( false ); + return true; + } + if ( back < emptyChunk_ ) + { + assert( false ); + return true; + } + if ( front > emptyChunk_ ) + { + assert( false ); + return true; + } + if ( !emptyChunk_->HasAvailable( numBlocks_ ) ) + { + // This may imply somebody tried to delete a block twice. + assert( false ); + return true; + } + break; + default: + assert( false ); + return true; + } + for ( ChunkCIter it( start ); it != last; ++it ) + { + const Chunk & chunk = *it; + if ( chunk.IsCorrupt( numBlocks_, blockSize_, true ) ) + return true; + } + } + + return false; +} + // FixedAllocator::HasBlock --------------------------------------------------- const Chunk * FixedAllocator::HasBlock( void * p ) const { const std::size_t chunkLength = numBlocks_ * blockSize_; - unsigned char * pc = static_cast< unsigned char * >( p ); for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it ) { const Chunk & chunk = *it; - if ( chunk.HasBlock( pc, chunkLength ) ) + if ( chunk.HasBlock( p, chunkLength ) ) return &chunk; } return NULL; @@ -601,9 +851,19 @@ bool FixedAllocator::Deallocate( void * p, Chunk * hint ) if ( NULL == foundChunk ) return false; - assert( foundChunk->HasBlock( static_cast< unsigned char * >( p ), - numBlocks_ * blockSize_ ) ); - assert( !foundChunk->IsCorrupt( numBlocks_, blockSize_ ) ); + assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) ); +#ifdef LOKI_CHECK_FOR_CORRUPTION + if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) ) + { + assert( false ); + return false; + } + if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) ) + { + assert( false ); + return false; + } +#endif deallocChunk_ = foundChunk; DoDeallocate(p); assert( CountEmptyChunks() < 2 ); @@ -618,9 +878,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const if ( chunks_.empty() ) return NULL; assert(deallocChunk_); - unsigned char * pc = static_cast< unsigned char * >( p ); const std::size_t chunkLength = numBlocks_ * blockSize_; - Chunk * lo = deallocChunk_; Chunk * hi = deallocChunk_ + 1; const Chunk * loBound = &chunks_.front(); @@ -633,7 +891,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const { if (lo) { - if ( lo->HasBlock( pc, chunkLength ) ) return lo; + if ( lo->HasBlock( p, chunkLength ) ) return lo; if ( lo == loBound ) { lo = NULL; @@ -644,7 +902,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const if (hi) { - if ( hi->HasBlock( pc, chunkLength ) ) return hi; + if ( hi->HasBlock( p, chunkLength ) ) return hi; if ( ++hi == hiBound ) { hi = NULL; @@ -660,8 +918,12 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const void FixedAllocator::DoDeallocate(void* p) { - assert( deallocChunk_->HasBlock( static_cast< unsigned char * >( p ), - numBlocks_ * blockSize_ ) ); + // Show that deallocChunk_ really owns the block at address p. + assert( deallocChunk_->HasBlock( p, numBlocks_ * blockSize_ ) ); + // Either of the next two assertions may fail if somebody tries to + // delete the same block twice. + assert( emptyChunk_ != deallocChunk_ ); + assert( !deallocChunk_->HasAvailable( numBlocks_ ) ); // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk. assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) ); @@ -874,6 +1136,34 @@ void SmallObjAllocator::Deallocate( void * p ) assert( found ); } +// SmallObjAllocator::IsCorrupt ----------------------------------------------- + +bool SmallObjAllocator::IsCorrupt( void ) const +{ + if ( NULL == pool_ ) + { + assert( false ); + return true; + } + if ( 0 == GetAlignment() ) + { + assert( false ); + return true; + } + if ( 0 == GetMaxObjectSize() ) + { + assert( false ); + return true; + } + const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() ); + for ( std::size_t ii = 0; ii < allocCount; ++ii ) + { + if ( pool_[ ii ].IsCorrupt() ) + return true; + } + return false; +} + } // end namespace Loki //////////////////////////////////////////////////////////////////////////////// @@ -887,6 +1177,10 @@ void SmallObjAllocator::Deallocate( void * p ) //////////////////////////////////////////////////////////////////////////////// // $Log$ +// Revision 1.18 2005/12/08 22:08:20 rich_sposato +// Added functions to check for corrupted Chunks and FixedAllocators. Made +// several minor coding changes. +// // Revision 1.17 2005/11/03 12:43:55 syntheticpp // more doxygen documentation, modules added //