mirror of
git://uclibc.org/uClibc.git
synced 2026-01-19 00:08:21 +08:00
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>
101 lines
2.7 KiB
C
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;
|
|
}
|