Browse Source

libc/minimal: fix realloc() allocated memory alignment

The definition for realloc() says that it should return a pointer
to the allocated memory which is suitably aligned for any built-in
type.

Turn sys_heap_realloc() into a sys_heap_aligned_realloc() and use it
with __alignof__(z_max_align_t) to implement realloc() with proper
memory alignment for any platform.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
pull/31896/head
Nicolas Pitre 5 years ago committed by Anas Nashif
parent
commit
c822e0abbd
  1. 7
      include/sys/sys_heap.h
  2. 4
      lib/libc/minimal/source/stdlib/malloc.c
  3. 22
      lib/os/heap.c
  4. 33
      tests/lib/heap/src/main.c

7
include/sys/sys_heap.h

@ -137,10 +137,15 @@ void sys_heap_free(struct sys_heap *h, void *mem); @@ -137,10 +137,15 @@ void sys_heap_free(struct sys_heap *h, void *mem);
*
* @param heap Heap from which to allocate
* @param ptr Original pointer returned from a previous allocation
* @param align Alignment in bytes, must be a power of two
* @param bytes Number of bytes requested for the new block
* @return Pointer to memory the caller can now use, or NULL
*/
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes);
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
size_t align, size_t bytes);
#define sys_heap_realloc(heap, ptr, bytes) \
sys_heap_aligned_realloc(heap, ptr, 0, bytes)
/** @brief Validate heap integrity
*

4
lib/libc/minimal/source/stdlib/malloc.c

@ -56,7 +56,9 @@ static int malloc_prepare(const struct device *unused) @@ -56,7 +56,9 @@ static int malloc_prepare(const struct device *unused)
void *realloc(void *ptr, size_t requested_size)
{
void *ret = sys_heap_realloc(&z_malloc_heap, ptr, requested_size);
void *ret = sys_heap_aligned_realloc(&z_malloc_heap, ptr,
__alignof__(z_max_align_t),
requested_size);
return ret == NULL ? ptr : ret;
}

22
lib/os/heap.c

@ -312,28 +312,34 @@ void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes) @@ -312,28 +312,34 @@ void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes)
return mem;
}
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
size_t align, size_t bytes)
{
struct z_heap *h = heap->heap;
/* special realloc semantics */
if (ptr == NULL) {
return sys_heap_alloc(heap, bytes);
return sys_heap_aligned_alloc(heap, align, bytes);
}
if (bytes == 0) {
sys_heap_free(heap, ptr);
return NULL;
}
__ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
if (size_too_big(h, bytes)) {
return NULL;
}
chunkid_t c = mem_to_chunkid(h, ptr);
chunkid_t rc = right_chunk(h, c);
size_t chunks_need = bytes_to_chunksz(h, bytes);
size_t align_gap = (uint8_t *)ptr - (uint8_t *)chunk_mem(h, c);
size_t chunks_need = bytes_to_chunksz(h, bytes + align_gap);
if (chunk_size(h, c) == chunks_need) {
if (align && ((uintptr_t)ptr & (align - 1))) {
/* ptr is not sufficiently aligned */
} else if (chunk_size(h, c) == chunks_need) {
/* We're good already */
return ptr;
} else if (chunk_size(h, c) > chunks_need) {
@ -357,9 +363,10 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes) @@ -357,9 +363,10 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
merge_chunks(h, c, rc);
set_chunk_used(h, c, true);
return ptr;
} else {
/* Reallocate and copy */
void *ptr2 = sys_heap_alloc(heap, bytes);
}
/* Fallback: allocate and copy */
void *ptr2 = sys_heap_aligned_alloc(heap, align, bytes);
if (ptr2 == NULL) {
return NULL;
@ -369,7 +376,6 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes) @@ -369,7 +376,6 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
sys_heap_free(heap, ptr);
return ptr2;
}
}
void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes)
{

33
tests/lib/heap/src/main.c

@ -261,7 +261,7 @@ static void test_realloc(void) @@ -261,7 +261,7 @@ static void test_realloc(void)
zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(p1 != p2,
"Realloc should must have moved %p", p1);
"Realloc should have moved %p", p1);
zassert_true(realloc_check_block(p2, p2, 64), "data changed");
zassert_true(realloc_check_block(p3, p1, 64), "data changed");
@ -290,6 +290,37 @@ static void test_realloc(void) @@ -290,6 +290,37 @@ static void test_realloc(void)
"Realloc should have expanded in place %p -> %p",
p1, p3);
zassert_true(realloc_check_block(p3, p1, 61), "data changed");
/* Corner case with sys_heap_aligned_realloc() on 32-bit targets
* where actual memory doesn't match with given pointer
* (align_gap != 0).
*/
p1 = sys_heap_aligned_alloc(&heap, 8, 32);
realloc_fill_block(p1, 32);
p2 = sys_heap_alloc(&heap, 32);
realloc_fill_block(p2, 32);
p3 = sys_heap_aligned_realloc(&heap, p1, 8, 36);
zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(realloc_check_block(p3, p1, 32), "data changed");
zassert_true(realloc_check_block(p2, p2, 32), "data changed");
realloc_fill_block(p3, 36);
zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(p1 != p3,
"Realloc should have moved %p", p1);
/* Test realloc with increasing alignment */
p1 = sys_heap_aligned_alloc(&heap, 32, 32);
p2 = sys_heap_aligned_alloc(&heap, 8, 32);
p3 = sys_heap_aligned_realloc(&heap, p2, 8, 16);
zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(p2 == p3,
"Realloc should have expanded in place %p -> %p",
p2, p3);
p3 = sys_heap_aligned_alloc(&heap, 32, 8);
zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(p2 != p3,
"Realloc should have moved %p", p2);
}
void test_main(void)

Loading…
Cancel
Save