mirror of https://github.com/ipxe/ipxe.git
[malloc] Avoid integer overflow for excessively large memory allocations
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/41/head
parent
300a371bfb
commit
f3fbb5ff1c
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue