Collision between newlib aclocator and mbed allocator wrappers when MBED_HEAP_STATS_ENABLED is enabled
***Issue***
In _mbed_alloc_wrappers.cpp_, `__real__memalign_r` from newlib and this one internally calls `_malloc_r`, which is redirected to `__wrap__malloc_r`. When MbedStats are enabled, **malloc_wrapper()** add `alloc_info_t` and returns a shifted pointer. The `__real__memalign_r` expects a raw allocator pointer, so then reads invalid metadata, and can crash with BusFault.
That issue causes system crash durring GT test **mbed-usb-device-msd**. It can probably occurs on any target with DCACHE and MBED_ALL_STATS_ENABLED (that enable also MBED_HEAP_STATS_ENABLED).
**Proposed change** of __wrap__memalign_r and free_wrapper functions.
The `__wrap__memalign_r` will not call `__real__memalign_r` but directly calls `__real__malloc_r` with extra space, manually computes aligned pointerm, writes its own memalign-aware header and returns aligned pointer.
``` c++
#if MBED_HEAP_STATS_ENABLED
// Two signatures distinguish regular malloc allocations from memalign allocations in free_wrapper
#define MBED_HEAP_STATS_SIGNATURE (0xdeadbeef)
#define MBED_HEAP_STATS_MEMALIGN_SIGNATURE (MBED_HEAP_STATS_SIGNATURE +1)
extern "C" void free_wrapper(struct _reent *r, void *ptr, void *caller)
{
#if MBED_MEM_TRACING_ENABLED
mbed_mem_trace_lock();
#endif
#if MBED_HEAP_STATS_ENABLED
malloc_stats_mutex->lock();
alloc_info_t *alloc_info = NULL;
if (ptr != NULL) {
alloc_info = ((alloc_info_t *)ptr) - 1;
if (MBED_HEAP_STATS_SIGNATURE == alloc_info->signature ||
MBED_HEAP_STATS_MEMALIGN_SIGNATURE == alloc_info->signature) {
// For memalign allocations the raw base pointer is stashed in the
// word immediately before alloc_info; for regular malloc allocations
// alloc_info itself is the base.
void *ptr_to_free = (MBED_HEAP_STATS_MEMALIGN_SIGNATURE == alloc_info->signature)
? ((void **)alloc_info)[-1]
: (void *)alloc_info;
size_t user_size = alloc_info->size;
size_t alloc_size = get_malloc_block_total_size(ptr_to_free);
alloc_info->signature = 0x0;
heap_stats.current_size -= user_size;
heap_stats.alloc_cnt -= 1;
heap_stats.overhead_size -= (alloc_size - user_size);
__real__free_r(r, ptr_to_free);
} else {
__real__free_r(r, ptr);
}
}
malloc_stats_mutex->unlock();
#else // #if MBED_HEAP_STATS_ENABLED
__real__free_r(r, ptr);
#endif // #if MBED_HEAP_STATS_ENABLED
#if MBED_MEM_TRACING_ENABLED
mbed_mem_trace_free(ptr, caller);
mbed_mem_trace_unlock();
#endif // #if MBED_MEM_TRACING_ENABLED
}
extern "C" void *__wrap__memalign_r(struct _reent *r, size_t alignment, size_t bytes)
{
void *ptr = NULL;
#if MBED_MEM_TRACING_ENABLED
mbed_mem_trace_lock();
#endif
#if MBED_HEAP_STATS_ENABLED
// Header before aligned ptr: [void *raw_base] [alloc_info_t]
const size_t hdr = sizeof(void *) + sizeof(alloc_info_t);
size_t pad = (alignment > hdr) ? alignment : hdr;
void *raw = NULL;
if (bytes <= SIZE_MAX - pad) {
raw = __real__malloc_r(r, bytes + pad);
}
if (raw != NULL) {
uintptr_t min_user = (uintptr_t)raw + hdr;
uintptr_t user_addr = (min_user + alignment - 1u) & ~(uintptr_t)(alignment - 1u);
alloc_info_t *info = ((alloc_info_t *)user_addr) - 1;
info->size = bytes;
info->signature = MBED_HEAP_STATS_MEMALIGN_SIGNATURE;
((void **)info)[-1] = raw; // stash raw base for free_wrapper
ptr = (void *)user_addr;
malloc_stats_mutex->lock();
heap_stats.current_size += bytes;
heap_stats.total_size += bytes;
heap_stats.alloc_cnt += 1;
if (heap_stats.current_size > heap_stats.max_size) {
heap_stats.max_size = heap_stats.current_size;
}
heap_stats.overhead_size += get_malloc_block_total_size(raw) - bytes;
malloc_stats_mutex->unlock();
} else {
malloc_stats_mutex->lock();
heap_stats.alloc_fail_cnt += 1;
malloc_stats_mutex->unlock();
}
#else // #if MBED_HEAP_STATS_ENABLED
ptr = __real__memalign_r(r, alignment, bytes);
#endif // #if MBED_HEAP_STATS_ENABLED
#if MBED_MEM_TRACING_ENABLED
mbed_mem_trace_malloc(ptr, bytes, MBED_CALLER_ADDR());
mbed_mem_trace_unlock();
#endif // #if MBED_MEM_TRACING_ENABLED
return ptr;
}
```
1 条评论