david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[malloc] Check integrity of free list

Check the integrity of the free memory block list before and after any
modifications to the list.  We check that certain invariants are
preserved:

 - the list is a well-formed doubly linked list

 - all blocks are at least MIN_MEMBLOCK_SIZE

 - no block extends beyond the end of our address space

 - blocks remain sorted in ascending order of address

 - no blocks are adjacent (i.e. any adjacent blocks have been merged)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2014-12-15 14:45:05 +00:00
parent 9154f2aef3
commit 7871666740
1 changed files with 59 additions and 1 deletions

View File

@ -186,6 +186,42 @@ static inline void valgrind_make_blocks_noaccess ( void ) {
VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) );
}
/**
* Check integrity of the blocks in the free list
*
*/
static inline void check_blocks ( void ) {
struct memory_block *block;
struct memory_block *prev = NULL;
if ( ! ASSERTING )
return;
list_for_each_entry ( block, &free_blocks, list ) {
/* Check that list structure is intact */
list_check ( &block->list );
/* Check that block size is not too small */
assert ( block->size >= sizeof ( *block ) );
assert ( block->size >= MIN_MEMBLOCK_SIZE );
/* Check that block does not wrap beyond end of address space */
assert ( ( ( void * ) block + block->size ) >
( ( void * ) block ) );
/* Check that blocks remain in ascending order, and
* that adjacent blocks have been merged.
*/
if ( prev ) {
assert ( ( ( void * ) block ) > ( ( void * ) prev ) );
assert ( ( ( void * ) block ) >
( ( ( void * ) prev ) + prev->size ) );
}
prev = block;
}
}
/**
* Discard some cached data
*
@ -242,6 +278,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) );
valgrind_make_blocks_defined();
check_blocks();
/* Round up size to multiple of MIN_MEMBLOCK_SIZE and
* calculate alignment mask.
@ -314,6 +351,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
}
done:
check_blocks();
valgrind_make_blocks_noaccess();
return ptr;
}
@ -338,6 +376,7 @@ void free_memblock ( void *ptr, size_t size ) {
return;
valgrind_make_blocks_defined();
check_blocks();
/* Round up size to match actual size that alloc_memblock()
* would have used.
@ -346,11 +385,29 @@ void free_memblock ( void *ptr, size_t size ) {
size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 );
freeing = ptr;
VALGRIND_MAKE_MEM_DEFINED ( freeing, sizeof ( *freeing ) );
freeing->size = size;
DBGC2 ( &heap, "Freeing [%p,%p)\n",
freeing, ( ( ( void * ) freeing ) + size ) );
/* Check that this block does not overlap the free list */
if ( ASSERTING ) {
list_for_each_entry ( block, &free_blocks, list ) {
if ( ( ( ( void * ) block ) <
( ( void * ) freeing + size ) ) &&
( ( void * ) freeing <
( ( void * ) block + block->size ) ) ) {
assert ( 0 );
DBGC ( &heap, "Double free of [%p,%p) "
"overlapping [%p,%p) detected from %p\n",
freeing,
( ( ( void * ) freeing ) + size ), block,
( ( void * ) block + block->size ),
__builtin_return_address ( 0 ) );
}
}
}
/* Insert/merge into free list */
freeing->size = size;
list_for_each_entry_safe ( block, tmp, &free_blocks, list ) {
/* Calculate gaps before and after the "freeing" block */
gap_before = ( ( ( void * ) freeing ) -
@ -392,6 +449,7 @@ void free_memblock ( void *ptr, size_t size ) {
/* Update free memory counter */
freemem += size;
check_blocks();
valgrind_make_blocks_noaccess();
}