diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index 962fcd1a90..f3ac9460ee 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -24,8 +24,11 @@ #include "esp_system.h" -// forward declaration -IRAM_ATTR static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps); +/* Forward declaration for base function, put in IRAM. + * These functions don't check for errors after trying to allocate memory. */ +static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps ); +static void *heap_caps_calloc_base( size_t n, size_t size, uint32_t caps ); +static void *heap_caps_malloc_base( size_t size, uint32_t caps ); /* This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM @@ -67,9 +70,9 @@ static void heap_caps_alloc_failed(size_t requested_size, uint32_t caps, const c alloc_failed_callback(requested_size, caps, function_name); } - #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS +#ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS esp_system_abort("Memory allocation failed"); - #endif +#endif } esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback) @@ -97,6 +100,10 @@ IRAM_ATTR static void *heap_caps_malloc_base( size_t size, uint32_t caps) { void *ret = NULL; + if (size == 0) { + return NULL; + } + if (size > HEAP_SIZE_MAX) { // Avoids int overflow when adding small numbers to size, or // calculating 'end' from start+size, by limiting 'size' to the possible range @@ -166,7 +173,7 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps){ void* ptr = heap_caps_malloc_base(size, caps); - if (!ptr){ + if (!ptr && size > 0){ heap_caps_alloc_failed(size, caps, __func__); } @@ -201,13 +208,13 @@ IRAM_ATTR void *heap_caps_malloc_default( size_t size ) } else { r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); } - if (r==NULL) { + if (r==NULL && size > 0) { //try again while being less picky r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT ); } // allocation failure? - if (r==NULL){ + if (r==NULL && size > 0){ heap_caps_alloc_failed(size, MALLOC_CAP_DEFAULT, __func__); } @@ -256,13 +263,17 @@ IRAM_ATTR void *heap_caps_malloc_prefer( size_t size, size_t num, ... ) va_list argp; va_start( argp, num ); void *r = NULL; + uint32_t caps = MALLOC_CAP_DEFAULT; while (num--) { - uint32_t caps = va_arg( argp, uint32_t ); - r = heap_caps_malloc( size, caps ); - if (r != NULL) { + caps = va_arg( argp, uint32_t ); + r = heap_caps_malloc_base( size, caps ); + if (r != NULL || size == 0) { break; } } + if (r == NULL && size > 0){ + heap_caps_alloc_failed(size, caps, __func__); + } va_end( argp ); return r; } @@ -275,13 +286,17 @@ IRAM_ATTR void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, .. va_list argp; va_start( argp, num ); void *r = NULL; + uint32_t caps = MALLOC_CAP_DEFAULT; while (num--) { - uint32_t caps = va_arg( argp, uint32_t ); - r = heap_caps_realloc( ptr, size, caps ); + caps = va_arg( argp, uint32_t ); + r = heap_caps_realloc_base( ptr, size, caps ); if (r != NULL || size == 0) { break; } } + if (r == NULL && size > 0){ + heap_caps_alloc_failed(size, caps, __func__); + } va_end( argp ); return r; } @@ -294,10 +309,16 @@ IRAM_ATTR void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... va_list argp; va_start( argp, num ); void *r = NULL; + uint32_t caps = MALLOC_CAP_DEFAULT; while (num--) { - uint32_t caps = va_arg( argp, uint32_t ); - r = heap_caps_calloc( n, size, caps ); - if (r != NULL) break; + caps = va_arg( argp, uint32_t ); + r = heap_caps_calloc_base( n, size, caps ); + if (r != NULL || size == 0){ + break; + } + } + if (r == NULL && size > 0){ + heap_caps_alloc_failed(size, caps, __func__); } va_end( argp ); return r; @@ -429,7 +450,11 @@ IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps) return ptr; } -IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) +/* +This function should not be called directly as it does not +check for failure / call heap_caps_alloc_failed() +*/ +IRAM_ATTR static void *heap_caps_calloc_base( size_t n, size_t size, uint32_t caps) { void *result; size_t size_bytes; @@ -438,13 +463,24 @@ IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) return NULL; } - result = heap_caps_malloc(size_bytes, caps); + result = heap_caps_malloc_base(size_bytes, caps); if (result != NULL) { bzero(result, size_bytes); } return result; } +IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) +{ + void* ptr = heap_caps_calloc_base(n, size, caps); + + if (!ptr && size > 0){ + heap_caps_alloc_failed(size, caps, __func__); + } + + return ptr; +} + size_t heap_caps_get_total_size(uint32_t caps) { size_t total_size = 0; @@ -599,6 +635,10 @@ IRAM_ATTR void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t return NULL; } + if (size == 0) { + return NULL; + } + if (size > HEAP_SIZE_MAX) { // Avoids int overflow when adding small numbers to size, or // calculating 'end' from start+size, by limiting 'size' to the possible range diff --git a/components/heap/test/test_malloc.c b/components/heap/test/test_malloc.c index 6f4bbd8728..995a64da33 100644 --- a/components/heap/test/test_malloc.c +++ b/components/heap/test/test_malloc.c @@ -132,3 +132,25 @@ TEST_CASE("malloc(0) should return a NULL pointer", "[heap]") p = malloc(0); TEST_ASSERT(p == NULL); } + +static bool failure_occured = false; + +static void test_alloc_failure_callback(size_t size, uint32_t caps, const char * function_name) +{ + failure_occured = true; +} + +TEST_CASE("malloc/calloc(0) should not call failure callback", "[heap]") +{ + void* ptr = NULL; + esp_err_t ret = heap_caps_register_failed_alloc_callback(test_alloc_failure_callback); + TEST_ASSERT(ret == ESP_OK); + ptr = malloc(0); + TEST_ASSERT_NULL(ptr); + /* Check that our callback was NOT called */ + TEST_ASSERT_FALSE(failure_occured); + /* Do the same thing for calloc */ + ptr = calloc(0, 0); + TEST_ASSERT_NULL(ptr); + TEST_ASSERT_FALSE(failure_occured); +}