diff --git a/components/esp_rom/include/esp_rom_multi_heap.h b/components/esp_rom/include/esp_rom_multi_heap.h index b95ce4607d..5289be65cb 100644 --- a/components/esp_rom/include/esp_rom_multi_heap.h +++ b/components/esp_rom/include/esp_rom_multi_heap.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -38,6 +38,20 @@ typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int b */ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data); +/** + * @brief Function walking through a given heap and returning the pointer to the + * allocated block containing the pointer passed as parameter. + * + * @note The heap parameter must be valid and the pointer parameter must + * belong to a block of allocated memory. The app will crash with an + * assertion failure if at least one of the parameter is invalid. + * + * @param heap The heap to walk through + * @param ptr The pointer to find the allocated block of + * @return Pointer to the allocated block containing the pointer ptr + */ +void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr); + #ifdef __cplusplus } #endif diff --git a/components/esp_rom/patches/esp_rom_multi_heap.c b/components/esp_rom/patches/esp_rom_multi_heap.c index 99bf6b45da..34d5109696 100644 --- a/components/esp_rom/patches/esp_rom_multi_heap.c +++ b/components/esp_rom/patches/esp_rom_multi_heap.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ #include #include +#include "sdkconfig.h" #include "esp_rom_multi_heap.h" // Hook to force the linker to include this file @@ -49,3 +50,101 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data); multi_heap_internal_unlock(heap); } + +/** + * @brief Structure used in multi_heap_find_containing_block to retain + * information while walking a given heap to find the allocated block + * containing the pointer ptr. + * + * @note The block_ptr gets filled with the pointer to the allocated block + * containing the ptr. + */ +typedef struct containing_block_data { + void *ptr; ///< Pointer to find the containing block of + void *block_ptr; ///< Pointer to the containing block +} containing_block_data_t; + + +#if CONFIG_HEAP_POISONING_DISABLED + void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr) + __attribute__((alias("multi_heap_find_containing_block_impl"))); +#endif + +typedef struct block_header_t +{ + /* Points to the previous physical block. */ + struct block_header_t* prev_phys_block; + + /* The size of this block, excluding the block header. */ + size_t size; + + /* Next and previous free blocks. */ + struct block_header_t* next_free; + struct block_header_t* prev_free; +} block_header_t; + +#define tlsf_cast(t, exp) ((t) (exp)) +#define block_header_free_bit (1UL << 0) +#define block_header_prev_free_bit (1UL << 1) +#define block_header_overhead (sizeof(size_t)) +#define block_start_offset (offsetof(block_header_t, size) + sizeof(size_t)) + +#if !defined (tlsf_assert) +#define tlsf_assert assert +#endif + +static inline __attribute__((always_inline)) void* block_to_ptr(const block_header_t* block) +{ + return tlsf_cast(void*, + tlsf_cast(unsigned char*, block) + block_start_offset); +} +static size_t block_size(const block_header_t* block) +{ + return block->size & ~(block_header_free_bit | block_header_prev_free_bit); +} +static block_header_t* offset_to_block(const void* ptr, size_t size) +{ + return tlsf_cast(block_header_t*, tlsf_cast(ptrdiff_t, ptr) + size); +} +static int block_is_free(const block_header_t* block) +{ + return tlsf_cast(int, block->size & block_header_free_bit); +} +static int block_is_last(const block_header_t* block) +{ + return block_size(block) == 0; +} +static block_header_t* block_next(const block_header_t* block) +{ + block_header_t* next = offset_to_block(block_to_ptr(block), + block_size(block) - block_header_overhead); + tlsf_assert(!block_is_last(block)); + return next; +} + +void* tlsf_find_containing_block(pool_t pool, void *ptr) +{ + block_header_t* block = offset_to_block(pool, -(int)block_header_overhead); + + while (block && !block_is_last(block)) + { + if (!block_is_free(block)) { + void *block_end = block_to_ptr(block) + block_size(block); + if (block_to_ptr(block) <= ptr && block_end > ptr) { + // we found the containing block, return + return block_to_ptr(block); + } + } + + block = block_next(block); + } + + return NULL; +} + +void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr) +{ + void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr); + assert(block_ptr); + return block_ptr; +} diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index bad0e2b88f..d19f5bd76e 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -468,6 +468,15 @@ size_t heap_caps_get_allocated_size( void *ptr ) return MULTI_HEAP_REMOVE_BLOCK_OWNER_SIZE(size); } +size_t heap_caps_get_containing_block_size(void *ptr) +{ + heap_t *heap = find_containing_heap(ptr); + assert(heap); + + void *block_ptr = multi_heap_find_containing_block(heap->heap, ptr); + return multi_heap_get_allocated_size(heap->heap, block_ptr); +} + static HEAP_IRAM_ATTR esp_err_t heap_caps_aligned_check_args(size_t alignment, size_t size, uint32_t caps, const char *funcname) { if (!alignment) { @@ -567,7 +576,7 @@ typedef struct walker_data { heap_t *heap; } walker_data_t; -__attribute__((noinline)) static bool heap_caps_walker(void* block_ptr, size_t block_size, int block_used, void *user_data) +__attribute__((noinline)) static bool heap_caps_walker(void *block_ptr, size_t block_size, int block_used, void *user_data) { walker_data_t *walker_data = (walker_data_t*)user_data; diff --git a/components/heap/heap_caps_linux.c b/components/heap/heap_caps_linux.c index beee84a9f4..c7b558e766 100644 --- a/components/heap/heap_caps_linux.c +++ b/components/heap/heap_caps_linux.c @@ -195,11 +195,15 @@ void heap_caps_dump_all(void) heap_caps_dump(MALLOC_CAP_INVALID); } -size_t heap_caps_get_allocated_size( void *ptr ) +size_t heap_caps_get_allocated_size(void *ptr) { return 0; } +size_t heap_caps_get_containing_block_size(void *ptr) { + return 0; +} + void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps) { void *ptr = aligned_alloc(alignment, size); diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index 0229809c18..c5aaed2fbc 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -446,7 +446,20 @@ void heap_caps_dump_all(void); * @return Size of the memory allocated at this block. * */ -size_t heap_caps_get_allocated_size( void *ptr ); +size_t heap_caps_get_allocated_size(void *ptr); + +/** + * @brief Return the size of the block containing the pointer passed as parameter. + * + * @param ptr Pointer to currently allocated heap memory. The pointer value + * must be within the allocated memory and the memory must not be freed. + * + * @note The app will crash with an assertion failure if the pointer is invalid. + * + * @return Size of the containing block allocated. + * + */ +size_t heap_caps_get_containing_block_size(void *ptr); /** * @brief Structure used to store heap related data passed to diff --git a/components/heap/include/multi_heap.h b/components/heap/include/multi_heap.h index 7aa348f72b..4fa76b1df5 100644 --- a/components/heap/include/multi_heap.h +++ b/components/heap/include/multi_heap.h @@ -239,6 +239,20 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun */ size_t multi_heap_get_full_block_size(multi_heap_handle_t heap, void *p); +/** + * @brief Function walking through a given heap and returning the pointer to the + * allocated block containing the pointer passed as parameter. + * + * @note The heap parameter must be valid and the pointer parameter must + * belong to a block of allocated memory. The app will crash with an + * assertion failure if at least one of the parameter is invalid. + * + * @param heap The heap to walk through + * @param ptr The pointer to find the allocated block of + * @return Pointer to the allocated block containing the pointer ptr + */ +void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr); + #ifdef __cplusplus } #endif diff --git a/components/heap/multi_heap.c b/components/heap/multi_heap.c index a342252720..bb58882ec0 100644 --- a/components/heap/multi_heap.c +++ b/components/heap/multi_heap.c @@ -72,6 +72,9 @@ size_t multi_heap_minimum_free_size(multi_heap_handle_t heap) void *multi_heap_get_block_address(multi_heap_block_handle_t block) __attribute__((alias("multi_heap_get_block_address_impl"))); +void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr) + __attribute__((alias("multi_heap_find_containing_block_impl"))); + #endif // !CONFIG_HEAP_TLSF_USE_ROM_IMPL #endif // !MULTI_HEAP_POISONING @@ -441,6 +444,26 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun multi_heap_internal_unlock(heap); } +/** + * @brief Structure used in multi_heap_find_containing_block to retain + * information while walking a given heap to find the allocated block + * containing the pointer ptr. + * + * @note The block_ptr gets filled with the pointer to the allocated block + * containing the ptr. + */ +typedef struct containing_block_data { + void *ptr; ///< Pointer to find the containing block of + void *block_ptr; ///< Pointer to the containing block +} containing_block_data_t; + +void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr) +{ + void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr); + assert(block_ptr); + return block_ptr; +} + #endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap) diff --git a/components/heap/multi_heap_internal.h b/components/heap/multi_heap_internal.h index 3fcbb50a76..789df9c75e 100644 --- a/components/heap/multi_heap_internal.h +++ b/components/heap/multi_heap_internal.h @@ -56,6 +56,7 @@ size_t multi_heap_free_size_impl(multi_heap_handle_t heap); size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap); size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p); void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block); +void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr); /* Some internal functions for heap poisoning use */ diff --git a/components/heap/multi_heap_poisoning.c b/components/heap/multi_heap_poisoning.c index 469f0684ae..462a9e980d 100644 --- a/components/heap/multi_heap_poisoning.c +++ b/components/heap/multi_heap_poisoning.c @@ -450,6 +450,15 @@ void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_fr memset(start, is_free ? FREE_FILL_PATTERN : MALLOC_FILL_PATTERN, size); } +void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr) +{ + void * block_ptr = multi_heap_find_containing_block_impl(heap, ptr); + // add the poison_head_t size to the pointer returned since all other + // functions expect the pointer to point to the first usable byte and not + // to the first allocated byte. + return block_ptr + sizeof(poison_head_t); +} + #else // !MULTI_HEAP_POISONING #ifdef MULTI_HEAP_POISONING_SLOW diff --git a/components/heap/test_apps/heap_tests/main/test_malloc.c b/components/heap/test_apps/heap_tests/main/test_malloc.c index dbfc5d4a6e..1289287e93 100644 --- a/components/heap/test_apps/heap_tests/main/test_malloc.c +++ b/components/heap/test_apps/heap_tests/main/test_malloc.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -210,9 +210,15 @@ TEST_CASE("test get allocated size", "[heap]") // since the heap component aligns to 4 bytes) const size_t aligned_size = (alloc_sizes[i] + 3) & ~3; const size_t real_size = heap_caps_get_allocated_size(ptr_array[i]); - printf("initial size: %d, requested size : %d, allocated size: %d\n", alloc_sizes[i], aligned_size, real_size); TEST_ASSERT(aligned_size <= real_size); + // test the heap_caps_get_containing_block_size() returns the right number of bytes + // when the pointer to the first, last (calculated from the requested size) and to a byte + // in the middle of the chunk is passed as parameter + TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i])); + TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + alloc_sizes[i])); + TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + (alloc_sizes[i] / 2))); + heap_caps_free(ptr_array[i]); } } diff --git a/components/heap/tlsf b/components/heap/tlsf index ba64d198a8..2867f6883a 160000 --- a/components/heap/tlsf +++ b/components/heap/tlsf @@ -1 +1 @@ -Subproject commit ba64d198a845df70b481e2c55004521ca643dea6 +Subproject commit 2867f6883a12920b1969ff9624c0ab0e4185c2ce