david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[malloc] Avoid integer overflow for excessively large memory allocations

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2015-09-29 01:07:08 +01:00
parent 300a371bfb
commit f3fbb5ff1c
1 changed files with 49 additions and 48 deletions

View File

@ -275,7 +275,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
size_t align_mask; size_t align_mask;
size_t actual_size; size_t actual_size;
size_t pre_size; size_t pre_size;
ssize_t post_size; size_t post_size;
struct memory_block *pre; struct memory_block *pre;
struct memory_block *post; struct memory_block *post;
void *ptr; void *ptr;
@ -291,7 +291,9 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
*/ */
actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) &
~( MIN_MEMBLOCK_SIZE - 1 ) ); ~( MIN_MEMBLOCK_SIZE - 1 ) );
assert ( actual_size >= size );
align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) );
assert ( ( actual_size + align_mask ) > actual_size );
DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n",
size, align, offset ); size, align, offset );
@ -300,55 +302,54 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) {
list_for_each_entry ( block, &free_blocks, list ) { list_for_each_entry ( block, &free_blocks, list ) {
pre_size = ( ( offset - virt_to_phys ( block ) ) pre_size = ( ( offset - virt_to_phys ( block ) )
& align_mask ); & align_mask );
if ( block->size < ( pre_size + actual_size ) )
continue;
post_size = ( block->size - pre_size - actual_size ); post_size = ( block->size - pre_size - actual_size );
if ( post_size >= 0 ) { /* Split block into pre-block, block, and
/* Split block into pre-block, block, and * post-block. After this split, the "pre"
* post-block. After this split, the "pre" * block is the one currently linked into the
* block is the one currently linked into the * free list.
* free list. */
*/ pre = block;
pre = block; block = ( ( ( void * ) pre ) + pre_size );
block = ( ( ( void * ) pre ) + pre_size ); post = ( ( ( void * ) block ) + actual_size );
post = ( ( ( void * ) block ) + actual_size ); DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre,
DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", ( ( ( void * ) pre ) + pre->size ), pre, block,
pre, ( ( ( void * ) pre ) + pre->size ), post, ( ( ( void * ) pre ) + pre->size ) );
pre, block, post, /* If there is a "post" block, add it in to
( ( ( void * ) pre ) + pre->size ) ); * the free list. Leak it if it is too small
/* If there is a "post" block, add it in to * (which can happen only at the very end of
* the free list. Leak it if it is too small * the heap).
* (which can happen only at the very end of */
* the heap). if ( post_size >= MIN_MEMBLOCK_SIZE ) {
*/ VALGRIND_MAKE_MEM_UNDEFINED ( post,
if ( (size_t) post_size >= MIN_MEMBLOCK_SIZE ) { sizeof ( *post ));
VALGRIND_MAKE_MEM_UNDEFINED post->size = post_size;
( post, sizeof ( *post ) ); list_add ( &post->list, &pre->list );
post->size = post_size;
list_add ( &post->list, &pre->list );
}
/* Shrink "pre" block, leaving the main block
* isolated and no longer part of the free
* list.
*/
pre->size = pre_size;
/* If there is no "pre" block, remove it from
* the list. Also remove it (i.e. leak it) if
* it is too small, which can happen only at
* the very start of the heap.
*/
if ( pre_size < MIN_MEMBLOCK_SIZE ) {
list_del ( &pre->list );
VALGRIND_MAKE_MEM_NOACCESS
( pre, sizeof ( *pre ) );
}
/* Update total free memory */
freemem -= actual_size;
/* Return allocated block */
DBGC2 ( &heap, "Allocated [%p,%p)\n", block,
( ( ( void * ) block ) + size ) );
ptr = block;
VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size );
goto done;
} }
/* Shrink "pre" block, leaving the main block
* isolated and no longer part of the free
* list.
*/
pre->size = pre_size;
/* If there is no "pre" block, remove it from
* the list. Also remove it (i.e. leak it) if
* it is too small, which can happen only at
* the very start of the heap.
*/
if ( pre_size < MIN_MEMBLOCK_SIZE ) {
list_del ( &pre->list );
VALGRIND_MAKE_MEM_NOACCESS ( pre,
sizeof ( *pre ) );
}
/* Update total free memory */
freemem -= actual_size;
/* Return allocated block */
DBGC2 ( &heap, "Allocated [%p,%p)\n", block,
( ( ( void * ) block ) + size ) );
ptr = block;
VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size );
goto done;
} }
/* Try discarding some cached data to free up memory */ /* Try discarding some cached data to free up memory */