Skip to content

Commit 826c69f

Browse files
committed
Merge branch 'feat/heap-get-size-from-any-pointer' into 'master'
feat(heap): add API to get allocated size from any pointer Closes IDF-12764 See merge request espressif/esp-idf!34531
2 parents fc5bf27 + 8a987d9 commit 826c69f

File tree

10 files changed

+201
-9
lines changed

10 files changed

+201
-9
lines changed

components/esp_rom/include/esp_rom_multi_heap.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -38,6 +38,20 @@ typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int b
3838
*/
3939
void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data);
4040

41+
/**
42+
* @brief Function walking through a given heap and returning the pointer to the
43+
* allocated block containing the pointer passed as parameter.
44+
*
45+
* @note The heap parameter must be valid and the pointer parameter must
46+
* belong to a block of allocated memory. The app will crash with an
47+
* assertion failure if at least one of the parameter is invalid.
48+
*
49+
* @param heap The heap to walk through
50+
* @param ptr The pointer to find the allocated block of
51+
* @return Pointer to the allocated block containing the pointer ptr
52+
*/
53+
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr);
54+
4155
#ifdef __cplusplus
4256
}
4357
#endif

components/esp_rom/patches/esp_rom_multi_heap.c

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -14,6 +14,7 @@
1414
#include <string.h>
1515
#include <assert.h>
1616

17+
#include "sdkconfig.h"
1718
#include "esp_rom_multi_heap.h"
1819

1920
// 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
4950
tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data);
5051
multi_heap_internal_unlock(heap);
5152
}
53+
54+
/**
55+
* @brief Structure used in multi_heap_find_containing_block to retain
56+
* information while walking a given heap to find the allocated block
57+
* containing the pointer ptr.
58+
*
59+
* @note The block_ptr gets filled with the pointer to the allocated block
60+
* containing the ptr.
61+
*/
62+
typedef struct containing_block_data {
63+
void *ptr; ///< Pointer to find the containing block of
64+
void *block_ptr; ///< Pointer to the containing block
65+
} containing_block_data_t;
66+
67+
68+
#if CONFIG_HEAP_POISONING_DISABLED
69+
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
70+
__attribute__((alias("multi_heap_find_containing_block_impl")));
71+
#endif
72+
73+
typedef struct block_header_t
74+
{
75+
/* Points to the previous physical block. */
76+
struct block_header_t* prev_phys_block;
77+
78+
/* The size of this block, excluding the block header. */
79+
size_t size;
80+
81+
/* Next and previous free blocks. */
82+
struct block_header_t* next_free;
83+
struct block_header_t* prev_free;
84+
} block_header_t;
85+
86+
#define tlsf_cast(t, exp) ((t) (exp))
87+
#define block_header_free_bit (1UL << 0)
88+
#define block_header_prev_free_bit (1UL << 1)
89+
#define block_header_overhead (sizeof(size_t))
90+
#define block_start_offset (offsetof(block_header_t, size) + sizeof(size_t))
91+
92+
#if !defined (tlsf_assert)
93+
#define tlsf_assert assert
94+
#endif
95+
96+
static inline __attribute__((always_inline)) void* block_to_ptr(const block_header_t* block)
97+
{
98+
return tlsf_cast(void*,
99+
tlsf_cast(unsigned char*, block) + block_start_offset);
100+
}
101+
static size_t block_size(const block_header_t* block)
102+
{
103+
return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
104+
}
105+
static block_header_t* offset_to_block(const void* ptr, size_t size)
106+
{
107+
return tlsf_cast(block_header_t*, tlsf_cast(ptrdiff_t, ptr) + size);
108+
}
109+
static int block_is_free(const block_header_t* block)
110+
{
111+
return tlsf_cast(int, block->size & block_header_free_bit);
112+
}
113+
static int block_is_last(const block_header_t* block)
114+
{
115+
return block_size(block) == 0;
116+
}
117+
static block_header_t* block_next(const block_header_t* block)
118+
{
119+
block_header_t* next = offset_to_block(block_to_ptr(block),
120+
block_size(block) - block_header_overhead);
121+
tlsf_assert(!block_is_last(block));
122+
return next;
123+
}
124+
125+
void* tlsf_find_containing_block(pool_t pool, void *ptr)
126+
{
127+
block_header_t* block = offset_to_block(pool, -(int)block_header_overhead);
128+
129+
while (block && !block_is_last(block))
130+
{
131+
if (!block_is_free(block)) {
132+
void *block_end = block_to_ptr(block) + block_size(block);
133+
if (block_to_ptr(block) <= ptr && block_end > ptr) {
134+
// we found the containing block, return
135+
return block_to_ptr(block);
136+
}
137+
}
138+
139+
block = block_next(block);
140+
}
141+
142+
return NULL;
143+
}
144+
145+
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr)
146+
{
147+
void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr);
148+
assert(block_ptr);
149+
return block_ptr;
150+
}

components/heap/heap_caps.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -468,6 +468,15 @@ size_t heap_caps_get_allocated_size( void *ptr )
468468
return MULTI_HEAP_REMOVE_BLOCK_OWNER_SIZE(size);
469469
}
470470

471+
size_t heap_caps_get_containing_block_size(void *ptr)
472+
{
473+
heap_t *heap = find_containing_heap(ptr);
474+
assert(heap);
475+
476+
void *block_ptr = multi_heap_find_containing_block(heap->heap, ptr);
477+
return multi_heap_get_allocated_size(heap->heap, block_ptr);
478+
}
479+
471480
static HEAP_IRAM_ATTR esp_err_t heap_caps_aligned_check_args(size_t alignment, size_t size, uint32_t caps, const char *funcname)
472481
{
473482
if (!alignment) {
@@ -567,7 +576,7 @@ typedef struct walker_data {
567576
heap_t *heap;
568577
} walker_data_t;
569578

570-
__attribute__((noinline)) static bool heap_caps_walker(void* block_ptr, size_t block_size, int block_used, void *user_data)
579+
__attribute__((noinline)) static bool heap_caps_walker(void *block_ptr, size_t block_size, int block_used, void *user_data)
571580
{
572581
walker_data_t *walker_data = (walker_data_t*)user_data;
573582

components/heap/heap_caps_linux.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,15 @@ void heap_caps_dump_all(void)
195195
heap_caps_dump(MALLOC_CAP_INVALID);
196196
}
197197

198-
size_t heap_caps_get_allocated_size( void *ptr )
198+
size_t heap_caps_get_allocated_size(void *ptr)
199199
{
200200
return 0;
201201
}
202202

203+
size_t heap_caps_get_containing_block_size(void *ptr) {
204+
return 0;
205+
}
206+
203207
void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps)
204208
{
205209
void *ptr = aligned_alloc(alignment, size);

components/heap/include/esp_heap_caps.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -446,7 +446,20 @@ void heap_caps_dump_all(void);
446446
* @return Size of the memory allocated at this block.
447447
*
448448
*/
449-
size_t heap_caps_get_allocated_size( void *ptr );
449+
size_t heap_caps_get_allocated_size(void *ptr);
450+
451+
/**
452+
* @brief Return the size of the block containing the pointer passed as parameter.
453+
*
454+
* @param ptr Pointer to currently allocated heap memory. The pointer value
455+
* must be within the allocated memory and the memory must not be freed.
456+
*
457+
* @note The app will crash with an assertion failure if the pointer is invalid.
458+
*
459+
* @return Size of the containing block allocated.
460+
*
461+
*/
462+
size_t heap_caps_get_containing_block_size(void *ptr);
450463

451464
/**
452465
* @brief Structure used to store heap related data passed to

components/heap/include/multi_heap.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,20 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun
239239
*/
240240
size_t multi_heap_get_full_block_size(multi_heap_handle_t heap, void *p);
241241

242+
/**
243+
* @brief Function walking through a given heap and returning the pointer to the
244+
* allocated block containing the pointer passed as parameter.
245+
*
246+
* @note The heap parameter must be valid and the pointer parameter must
247+
* belong to a block of allocated memory. The app will crash with an
248+
* assertion failure if at least one of the parameter is invalid.
249+
*
250+
* @param heap The heap to walk through
251+
* @param ptr The pointer to find the allocated block of
252+
* @return Pointer to the allocated block containing the pointer ptr
253+
*/
254+
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr);
255+
242256
#ifdef __cplusplus
243257
}
244258
#endif

components/heap/multi_heap.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
7272
void *multi_heap_get_block_address(multi_heap_block_handle_t block)
7373
__attribute__((alias("multi_heap_get_block_address_impl")));
7474

75+
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
76+
__attribute__((alias("multi_heap_find_containing_block_impl")));
77+
7578
#endif // !CONFIG_HEAP_TLSF_USE_ROM_IMPL
7679
#endif // !MULTI_HEAP_POISONING
7780

@@ -441,6 +444,26 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun
441444
multi_heap_internal_unlock(heap);
442445
}
443446

447+
/**
448+
* @brief Structure used in multi_heap_find_containing_block to retain
449+
* information while walking a given heap to find the allocated block
450+
* containing the pointer ptr.
451+
*
452+
* @note The block_ptr gets filled with the pointer to the allocated block
453+
* containing the ptr.
454+
*/
455+
typedef struct containing_block_data {
456+
void *ptr; ///< Pointer to find the containing block of
457+
void *block_ptr; ///< Pointer to the containing block
458+
} containing_block_data_t;
459+
460+
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr)
461+
{
462+
void *block_ptr = tlsf_find_containing_block(tlsf_get_pool(heap->heap_data), ptr);
463+
assert(block_ptr);
464+
return block_ptr;
465+
}
466+
444467
#endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL
445468

446469
size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap)

components/heap/multi_heap_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ size_t multi_heap_free_size_impl(multi_heap_handle_t heap);
5656
size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap);
5757
size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p);
5858
void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block);
59+
void *multi_heap_find_containing_block_impl(multi_heap_handle_t heap, void *ptr);
5960

6061
/* Some internal functions for heap poisoning use */
6162

components/heap/multi_heap_poisoning.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,15 @@ void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_fr
450450
memset(start, is_free ? FREE_FILL_PATTERN : MALLOC_FILL_PATTERN, size);
451451
}
452452

453+
void *multi_heap_find_containing_block(multi_heap_handle_t heap, void *ptr)
454+
{
455+
void * block_ptr = multi_heap_find_containing_block_impl(heap, ptr);
456+
// add the poison_head_t size to the pointer returned since all other
457+
// functions expect the pointer to point to the first usable byte and not
458+
// to the first allocated byte.
459+
return block_ptr + sizeof(poison_head_t);
460+
}
461+
453462
#else // !MULTI_HEAP_POISONING
454463

455464
#ifdef MULTI_HEAP_POISONING_SLOW

components/heap/test_apps/heap_tests/main/test_malloc.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Unlicense OR CC0-1.0
55
*/
@@ -210,9 +210,15 @@ TEST_CASE("test get allocated size", "[heap]")
210210
// since the heap component aligns to 4 bytes)
211211
const size_t aligned_size = (alloc_sizes[i] + 3) & ~3;
212212
const size_t real_size = heap_caps_get_allocated_size(ptr_array[i]);
213-
printf("initial size: %d, requested size : %d, allocated size: %d\n", alloc_sizes[i], aligned_size, real_size);
214213
TEST_ASSERT(aligned_size <= real_size);
215214

215+
// test the heap_caps_get_containing_block_size() returns the right number of bytes
216+
// when the pointer to the first, last (calculated from the requested size) and to a byte
217+
// in the middle of the chunk is passed as parameter
218+
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i]));
219+
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + alloc_sizes[i]));
220+
TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + (alloc_sizes[i] / 2)));
221+
216222
heap_caps_free(ptr_array[i]);
217223
}
218224
}

0 commit comments

Comments
 (0)