Files
uClibc/libc/stdlib/malloc/realloc.c
Mike Frysinger 07e0ce9fa7 malloc: handle size overflows in realloc()
The malloc() code checks the incoming size to make sure the header
adjustment doesn't cause overflow in the size storage.  Add the same
check to realloc() to catch stupid stuff like realloc(..., -1).

Reported-by: James Coleman <james.coleman@ubicom.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-10-15 19:47:12 -04:00

101 lines
2.7 KiB
C

/*
* libc/stdlib/malloc/realloc.c -- realloc function
*
* Copyright (C) 2002 NEC Corporation
* Copyright (C) 2002 Miles Bader <miles@gnu.org>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License. See the file COPYING.LIB in the main
* directory of this archive for more details.
*
* Written by Miles Bader <miles@gnu.org>
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "malloc.h"
#include "heap.h"
void *
realloc (void *mem, size_t new_size)
{
size_t size;
char *base_mem;
/* Check for special cases. */
if (! new_size)
{
free (mem);
return malloc (new_size);
}
if (! mem)
return malloc (new_size);
/* This matches the check in malloc() */
if (unlikely(((unsigned long)new_size > (unsigned long)(MALLOC_HEADER_SIZE*-2))))
return NULL;
/* Normal realloc. */
base_mem = MALLOC_BASE (mem);
size = MALLOC_SIZE (mem);
/* Include extra space to record the size of the allocated block.
Also make sure that we're dealing in a multiple of the heap
allocation unit (SIZE is already guaranteed to be so).*/
new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE);
if (new_size < sizeof (struct heap_free_area))
/* Because we sometimes must use a freed block to hold a free-area node,
we must make sure that every allocated block can hold one. */
new_size = HEAP_ADJUST_SIZE (sizeof (struct heap_free_area));
MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)",
(long)mem, new_size, (long)base_mem, size);
if (new_size > size)
/* Grow the block. */
{
size_t extra = new_size - size;
__heap_lock (&__malloc_heap_lock);
extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra);
__heap_unlock (&__malloc_heap_lock);
if (extra)
/* Record the changed size. */
MALLOC_SET_SIZE (base_mem, size + extra);
else
/* Our attempts to extend MEM in place failed, just
allocate-and-copy. */
{
void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE);
if (new_mem)
{
memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE);
free (mem);
}
mem = new_mem;
}
}
else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size)
/* Shrink the block. */
{
__heap_lock (&__malloc_heap_lock);
__heap_free (&__malloc_heap, base_mem + new_size, size - new_size);
__heap_unlock (&__malloc_heap_lock);
MALLOC_SET_SIZE (base_mem, new_size);
}
if (mem)
MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)",
(long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem));
else
MALLOC_DEBUG (-1, "realloc: returning 0");
return mem;
}