]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[malloc] Check integrity of free list
authorMichael Brown <mcb30@ipxe.org>
Mon, 15 Dec 2014 14:45:05 +0000 (14:45 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 15 Dec 2014 14:45:05 +0000 (14:45 +0000)
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>
src/core/malloc.c

index 0993bdac75b41e73d8b47da79081c1e6b27c9185..cd925e7c1fb4a8d3865714ca6e6c16f65c999db2 100644 (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();
 }