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. 32
      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);
* *
* @param heap Heap from which to allocate * @param heap Heap from which to allocate
* @param ptr Original pointer returned from a previous allocation * @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 * @param bytes Number of bytes requested for the new block
* @return Pointer to memory the caller can now use, or NULL * @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 /** @brief Validate heap integrity
* *

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

@ -56,7 +56,9 @@ static int malloc_prepare(const struct device *unused)
void *realloc(void *ptr, size_t requested_size) 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; return ret == NULL ? ptr : ret;
} }

32
lib/os/heap.c

@ -312,28 +312,34 @@ void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes)
return mem; 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; struct z_heap *h = heap->heap;
/* special realloc semantics */ /* special realloc semantics */
if (ptr == NULL) { if (ptr == NULL) {
return sys_heap_alloc(heap, bytes); return sys_heap_aligned_alloc(heap, align, bytes);
} }
if (bytes == 0) { if (bytes == 0) {
sys_heap_free(heap, ptr); sys_heap_free(heap, ptr);
return NULL; return NULL;
} }
__ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
if (size_too_big(h, bytes)) { if (size_too_big(h, bytes)) {
return NULL; return NULL;
} }
chunkid_t c = mem_to_chunkid(h, ptr); chunkid_t c = mem_to_chunkid(h, ptr);
chunkid_t rc = right_chunk(h, c); 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 */ /* We're good already */
return ptr; return ptr;
} else if (chunk_size(h, c) > chunks_need) { } else if (chunk_size(h, c) > chunks_need) {
@ -357,18 +363,18 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
merge_chunks(h, c, rc); merge_chunks(h, c, rc);
set_chunk_used(h, c, true); set_chunk_used(h, c, true);
return ptr; return ptr;
} else { }
/* Reallocate and copy */
void *ptr2 = sys_heap_alloc(heap, bytes);
if (ptr2 == NULL) { /* Fallback: allocate and copy */
return NULL; void *ptr2 = sys_heap_aligned_alloc(heap, align, bytes);
}
memcpy(ptr2, ptr, bytes); if (ptr2 == NULL) {
sys_heap_free(heap, ptr); return NULL;
return ptr2;
} }
memcpy(ptr2, ptr, bytes);
sys_heap_free(heap, ptr);
return ptr2;
} }
void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes) 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)
zassert_true(sys_heap_validate(&heap), "invalid heap"); zassert_true(sys_heap_validate(&heap), "invalid heap");
zassert_true(p1 != p2, 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(p2, p2, 64), "data changed");
zassert_true(realloc_check_block(p3, p1, 64), "data changed"); zassert_true(realloc_check_block(p3, p1, 64), "data changed");
@ -290,6 +290,37 @@ static void test_realloc(void)
"Realloc should have expanded in place %p -> %p", "Realloc should have expanded in place %p -> %p",
p1, p3); p1, p3);
zassert_true(realloc_check_block(p3, p1, 61), "data changed"); 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) void test_main(void)

Loading…
Cancel
Save