Merge branch 'fix/check_integrity_on_used_blocks' into 'master'

heap: fix integrity check on used blocks by the tlsf component

Closes IDFGH-11050

See merge request espressif/esp-idf!26289
This commit is contained in:
Guillaume Souchere 2023-10-18 15:47:25 +08:00
commit d6def6c06e
4 changed files with 77 additions and 6 deletions

View File

@ -8,8 +8,8 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
//This test only makes sense with poisoning enabled (light or comprehensive) // these tests only make sense with poisoning enabled (light or comprehensive)
#if defined(CONFIG_HEAP_POISONING_COMPREHENSIVE) || defined(CONFIG_HEAP_POISONING_LIGHT) #if defined(CONFIG_HEAP_POISONING_LIGHT) || defined(CONFIG_HEAP_POISONING_COMPREHENSIVE)
/* executing multi_heap_internal_check_block_poisoning() /* executing multi_heap_internal_check_block_poisoning()
* takes longer on external RAM and therefore the timeout * takes longer on external RAM and therefore the timeout
@ -70,4 +70,53 @@ TEST_CASE("multi_heap poisoning detection", "[heap]")
} }
} }
#endif #if !defined(CONFIG_HEAP_TLSF_USE_ROM_IMPL)
#ifdef CONFIG_HEAP_TASK_TRACKING
#define HEAD_CANARY_OFFSET 3 // head canary | task tracking | allocated size
#else
#define HEAD_CANARY_OFFSET 2 // head canary | allocated size
#endif // CONFIG_HEAP_TASK_TRACKING
#define TAIL_CANARY_OFFSET 1
/* This test will corrupt the canary of a allocated memory block and call the
* heap_caps_check_integrity() function to check that the corruption is detected.
*/
TEST_CASE("canary corruption in light or comprehensive poisoning mode", "[heap]")
{
const uint8_t allocation_size = 1 * sizeof(uint32_t);
/* malloc some memory to get a pointer */
uint32_t *ptr = heap_caps_malloc(allocation_size, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(ptr);
/* corrupt the head canary */
uint32_t canary = ptr[-HEAD_CANARY_OFFSET];
ptr[-HEAD_CANARY_OFFSET] = 0xdeadbeef;
/* call the integrity check function and verify that it returns 0 (corruption detected) */
bool is_corrupted = !heap_caps_check_integrity(MALLOC_CAP_DEFAULT, false);
TEST_ASSERT_TRUE(is_corrupted);
/* fix the head canary */
ptr[-HEAD_CANARY_OFFSET] = canary;
/* re run the corruption check to make sure the function returns no corruption */
is_corrupted = !heap_caps_check_integrity(MALLOC_CAP_DEFAULT, false);
TEST_ASSERT_FALSE(is_corrupted);
/* corrupt tail canary */
canary = ptr[TAIL_CANARY_OFFSET];
ptr[TAIL_CANARY_OFFSET] = 0xdeadbeef;
/* call the integrity check function and verify that it returns 0 (corruption detected) */
is_corrupted = !heap_caps_check_integrity(MALLOC_CAP_DEFAULT, false);
TEST_ASSERT_TRUE(is_corrupted);
/* clear the corruption and free the pointer before returning */
ptr[TAIL_CANARY_OFFSET] = canary;
heap_caps_free(ptr);
}
#endif // !CONFIG_HEAP_TLSF_USE_ROM_IMPL
#endif // CONFIG_HEAP_POISONING_LIGHT && CONFIG_HEAP_LIGHT_POISONING

@ -1 +1 @@
Subproject commit 8c9cd0517adf99e363812e9a295dfe3898fdd345 Subproject commit d2e28f872472ffc6a704faae65ddee1f24e2dfba

View File

@ -154,6 +154,11 @@ In both cases, the functions involve checking that the first 4 bytes of an alloc
Different values usually indicate buffer underrun or overrun. Overrun indicates that when writing to memory, the data written exceeds the size of the allocated memory, resulting in writing to an unallocated memory area; underrun indicates that when reading memory, the data read exceeds the allocated memory and reads data from an unallocated memory area. Different values usually indicate buffer underrun or overrun. Overrun indicates that when writing to memory, the data written exceeds the size of the allocated memory, resulting in writing to an unallocated memory area; underrun indicates that when reading memory, the data read exceeds the allocated memory and reads data from an unallocated memory area.
.. only:: CONFIG_ESP_ROM_HAS_HEAP_TLSF
.. warning::
When the ROM implementation of the heap TLSF is used, note that :cpp:func:`heap_caps_check_integrity` will not perform the check of the canary bytes.
Comprehensive Comprehensive
+++++++++++++ +++++++++++++
@ -179,6 +184,12 @@ Calls to :cpp:func:`heap_caps_check_integrity` may print errors relating to ``0x
- For free heap blocks, the checker expects to find all bytes set to ``0xFE``. Any other values indicate a use-after-free bug where free memory has been incorrectly overwritten. - For free heap blocks, the checker expects to find all bytes set to ``0xFE``. Any other values indicate a use-after-free bug where free memory has been incorrectly overwritten.
- For allocated heap blocks, the behavior is the same as for the Light Impact mode. The canary bytes ``0xABBA1234`` and ``0xBAAD5678`` are checked at the head and tail of each allocated buffer, and any variation indicates a buffer overrun or underrun. - For allocated heap blocks, the behavior is the same as for the Light Impact mode. The canary bytes ``0xABBA1234`` and ``0xBAAD5678`` are checked at the head and tail of each allocated buffer, and any variation indicates a buffer overrun or underrun.
.. only:: CONFIG_ESP_ROM_HAS_HEAP_TLSF
.. warning::
When the ROM implementation of the heap TLSF is used, note that :cpp:func:`heap_caps_check_integrity` will not perform the check of the canary bytes.
.. _heap-task-tracking: .. _heap-task-tracking:
Heap Task Tracking Heap Task Tracking

View File

@ -154,6 +154,11 @@ ESP-IDF 集成了用于请求 :ref:`堆内存信息 <heap-information>`、:ref:`
如果检查到字节与上述值不同,通常表示缓冲区越界或下溢。其中越界表示在写入内存时,写入的数据超过了所分配内存的大小,导致写入到了未分配的内存区域;下溢表示在读取内存时,读取的数据超出了所分配内存的范围,读取了未分配的内存区域的数据。 如果检查到字节与上述值不同,通常表示缓冲区越界或下溢。其中越界表示在写入内存时,写入的数据超过了所分配内存的大小,导致写入到了未分配的内存区域;下溢表示在读取内存时,读取的数据超出了所分配内存的范围,读取了未分配的内存区域的数据。
.. only:: CONFIG_ESP_ROM_HAS_HEAP_TLSF
.. warning::
需注意,使用 TLSF 堆的 ROM 实现时,:cpp:func:`heap_caps_check_integrity` 不会对 canary 字节执行检查。
全面检测模式 全面检测模式
+++++++++++++++++++ +++++++++++++++++++
@ -179,6 +184,12 @@ ESP-IDF 集成了用于请求 :ref:`堆内存信息 <heap-information>`、:ref:`
- 对于已释放的堆内存块,检测器会检查是否所有字节都设置为 ``0xFE``,检测到任何其他值都表示错误写入了已释放内存。 - 对于已释放的堆内存块,检测器会检查是否所有字节都设置为 ``0xFE``,检测到任何其他值都表示错误写入了已释放内存。
- 对于已分配的堆内存块,检测器的检查模式与轻微影响模式相同,即在每个分配的缓冲区头部和尾部检查 canary 字节 ``0xABBA1234````0xBAAD5678``,检测到任何其他字节都表示缓冲区越界或下溢。 - 对于已分配的堆内存块,检测器的检查模式与轻微影响模式相同,即在每个分配的缓冲区头部和尾部检查 canary 字节 ``0xABBA1234````0xBAAD5678``,检测到任何其他字节都表示缓冲区越界或下溢。
.. only:: CONFIG_ESP_ROM_HAS_HEAP_TLSF
.. warning::
需注意,使用 TLSF 堆的 ROM 实现时,:cpp:func:`heap_caps_check_integrity` 不会对 canary 字节执行检查。
.. _heap-task-tracking: .. _heap-task-tracking:
堆任务跟踪 堆任务跟踪
@ -280,7 +291,7 @@ ESP-IDF 集成了用于请求 :ref:`堆内存信息 <heap-information>`、:ref:`
total allocations 2 total frees 0 total allocations 2 total frees 0
.. note:: .. note::
以上示例输出使用 :doc:`IDF 监视器 </api-guides/tools/idf-monitor>`,自动将 PC 地址解码为其源文件和行号。 以上示例输出使用 :doc:`IDF 监视器 </api-guides/tools/idf-monitor>`,自动将 PC 地址解码为其源文件和行号。
第一行表示与缓冲区的总大小相比,缓冲区内的分配条目数量。 第一行表示与缓冲区的总大小相比,缓冲区内的分配条目数量。
@ -505,7 +516,7 @@ ESP-IDF 集成了用于请求 :ref:`堆内存信息 <heap-information>`、:ref:`
- 系统中的其他任务也可能进行内存分配。根据这些任务的时间安排,报错的这部分内存很可能在调用 :cpp:func:`heap_trace_stop` 后释放。 - 系统中的其他任务也可能进行内存分配。根据这些任务的时间安排,报错的这部分内存很可能在调用 :cpp:func:`heap_trace_stop` 后释放。
- 当任务第一次使用 stdio如调用 :cpp:func:`heap_caps_printf`libc 会分配一个锁,即 RTOS 互斥信号量,该分配将持续至任务删除。 - 当任务第一次使用 stdio如调用 :cpp:func:`heap_caps_printf`libc 会分配一个锁,即 RTOS 互斥信号量,该分配将持续至任务删除。
- 进行打印浮点数等调用 :cpp:func:`heap_caps_printf` 的操作时,会根据需要,从堆中分配一些内存,这些分配将持续至任务删除。 - 进行打印浮点数等调用 :cpp:func:`heap_caps_printf` 的操作时,会根据需要,从堆中分配一些内存,这些分配将持续至任务删除。
- 蓝牙、Wi-Fi 和 TCP/IP 库会分配堆内存缓冲区,处理传入或传出的数据,这些内存缓冲区通常持续时间较短。但如果在运行堆内存泄漏跟踪期间,网络底层接收或发送了数据,一些缓冲区可能会出现在堆内存泄漏跟踪输出中。 - 蓝牙、Wi-Fi 和 TCP/IP 库会分配堆内存缓冲区,处理传入或传出的数据,这些内存缓冲区通常持续时间较短。但如果在运行堆内存泄漏跟踪期间,网络底层接收或发送了数据,一些缓冲区可能会出现在堆内存泄漏跟踪输出中。
- 由于存在 ``TIME_WAIT`` 状态TCP 连接在关闭后仍会使用一些内存,``TIME_WAIT`` 状态结束后将释放这些内存。 - 由于存在 ``TIME_WAIT`` 状态TCP 连接在关闭后仍会使用一些内存,``TIME_WAIT`` 状态结束后将释放这些内存。
要区分“真实”和“误报”的内存泄漏,可以在堆内存跟踪运行时多次调用可疑代码,并在堆内存跟踪输出中查找重复出现的内存分配情况。 要区分“真实”和“误报”的内存泄漏,可以在堆内存跟踪运行时多次调用可疑代码,并在堆内存跟踪输出中查找重复出现的内存分配情况。