mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
fix(heap): preserve aligned allocation with KASAN
Account for the left redzone when aligning user pointers. Add regression coverage for KASAN-enabled allocations.
This commit is contained in:
@@ -40,6 +40,10 @@
|
|||||||
* The ordering is intentional: user underflows must hit the left redzone
|
* The ordering is intentional: user underflows must hit the left redzone
|
||||||
* before they can reach the task-tracking block-owner word.
|
* before they can reach the task-tracking block-owner word.
|
||||||
*
|
*
|
||||||
|
* For heap_caps_aligned_alloc(), the tlsf align offset must be
|
||||||
|
* (block-owner size + KASAN_RZ) so the *user* pointer (after both shifts)
|
||||||
|
* satisfies the requested alignment.
|
||||||
|
*
|
||||||
* Pointer arithmetic macros live in heap_kasan_layout.h.
|
* Pointer arithmetic macros live in heap_kasan_layout.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -183,6 +187,8 @@ HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_aligned_alloc_base(size_t alignment
|
|||||||
}
|
}
|
||||||
|
|
||||||
const size_t alloc_size = KASAN_ADD_RZ(size);
|
const size_t alloc_size = KASAN_ADD_RZ(size);
|
||||||
|
/* Align the eventual user pointer (after block-owner + left redzone). */
|
||||||
|
const size_t align_offset = MULTI_HEAP_BLOCK_OWNER_SIZE() + KASAN_RZ;
|
||||||
|
|
||||||
for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
|
for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
|
||||||
//Iterate over heaps and check capabilities at this priority
|
//Iterate over heaps and check capabilities at this priority
|
||||||
@@ -205,7 +211,7 @@ HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_aligned_alloc_base(size_t alignment
|
|||||||
//we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and
|
//we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and
|
||||||
//add a pointer to the DRAM equivalent before the address we're going to return.
|
//add a pointer to the DRAM equivalent before the address we're going to return.
|
||||||
ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(alloc_size) + 4,
|
ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(alloc_size) + 4,
|
||||||
alignment, MULTI_HEAP_BLOCK_OWNER_SIZE()); // int overflow checked above
|
alignment, align_offset); // int overflow checked above
|
||||||
if (ret != NULL) {
|
if (ret != NULL) {
|
||||||
#if CONFIG_HEAP_TASK_TRACKING
|
#if CONFIG_HEAP_TASK_TRACKING
|
||||||
heap_caps_update_per_task_info_alloc(heap,
|
heap_caps_update_per_task_info_alloc(heap,
|
||||||
@@ -224,7 +230,7 @@ HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_aligned_alloc_base(size_t alignment
|
|||||||
} else {
|
} else {
|
||||||
//Just try to alloc, nothing special.
|
//Just try to alloc, nothing special.
|
||||||
ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(alloc_size),
|
ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(alloc_size),
|
||||||
alignment, MULTI_HEAP_BLOCK_OWNER_SIZE());
|
alignment, align_offset);
|
||||||
if (ret != NULL) {
|
if (ret != NULL) {
|
||||||
#if CONFIG_HEAP_TASK_TRACKING
|
#if CONFIG_HEAP_TASK_TRACKING
|
||||||
heap_caps_update_per_task_info_alloc(heap,
|
heap_caps_update_per_task_info_alloc(heap,
|
||||||
|
|||||||
@@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_kasan.h"
|
#include "esp_kasan.h"
|
||||||
|
|
||||||
@@ -139,6 +141,34 @@ TEST_CASE("no false positive", "[kasan]")
|
|||||||
ESP_LOGI(TAG, "no-bug test PASSED");
|
ESP_LOGI(TAG, "no-bug test PASSED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
TEST_CASE("aligned allocation preserves alignment", "[kasan][heap]")
|
||||||
|
{
|
||||||
|
static const size_t alignments[] = { 8, 16, 32, 64 };
|
||||||
|
const uint32_t alloc_size = 37;
|
||||||
|
|
||||||
|
#if CONFIG_KASAN_NO_HALT
|
||||||
|
kasan_reset_error_count();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(alignments) / sizeof(alignments[0]); i++) {
|
||||||
|
const size_t alignment = alignments[i];
|
||||||
|
uint8_t *buf = heap_caps_aligned_alloc(alignment, alloc_size, MALLOC_CAP_DEFAULT);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_NULL(buf);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, (uintptr_t)buf & (alignment - 1));
|
||||||
|
/* Verify the full user region remains accessible after the KASAN redzone offset. */
|
||||||
|
memset(buf, 0xA5, alloc_size);
|
||||||
|
heap_caps_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_KASAN_NO_HALT
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, kasan_get_error_count());
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "aligned allocation test PASSED");
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
/*
|
/*
|
||||||
* GCC -fsanitize=kernel-address instrumentation calls a sized family of
|
* GCC -fsanitize=kernel-address instrumentation calls a sized family of
|
||||||
|
|||||||
@@ -105,6 +105,16 @@ def test_kasan_halt_no_false_positive(dut: Dut) -> None:
|
|||||||
dut.expect('no-bug test PASSED', timeout=15)
|
dut.expect('no-bug test PASSED', timeout=15)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||||
|
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||||
|
def test_kasan_halt_aligned_alloc(dut: Dut) -> None:
|
||||||
|
"""KASAN redzones must not change aligned allocator guarantees."""
|
||||||
|
dut.expect('KASAN test application starting', timeout=15)
|
||||||
|
dut.write('"aligned allocation preserves alignment"')
|
||||||
|
dut.expect('aligned allocation test PASSED', timeout=15)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.generic
|
@pytest.mark.generic
|
||||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user