Skip to content

Commit

Permalink
heap: Add test to check that the corruption of free memory is detected
Browse files Browse the repository at this point in the history
This commit extends the heap test set by adding a test to check corruption
detection in free memory block.

For each byte of the free block memory, the test changes the value of the byte,
call multi_heap_check(), make sure that the function returns 'corruption detected'
only when comprehensive poisoning is set, restore the good value of the byte, calls
multi_heap_check() again and make sure that it returns 'OK'.
  • Loading branch information
SoucheSouche committed Aug 26, 2022
1 parent b9abad7 commit 4571e19
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
64 changes: 64 additions & 0 deletions components/heap/test/test_corruption_check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "stdio.h"

#include "esp_heap_caps.h"

/* executing multi_heap_internal_check_block_poisoning()
* takes longer on external RAM and therefore the timeout
* in the test of 30 seconds is exceeded. Execute the test
* on a smaller memory chunk
*/
#ifdef CONFIG_SPIRAM
const size_t MALLOC_SIZE = 16;
#else
const size_t MALLOC_SIZE = 64;
#endif
const uint8_t CORRUPTED_VALUE = 0xaa;

/* This test will corrupt the memory of a free block in the heap and check
* that in the case of comprehensive poisoning the heap corruption is detected
* by heap_caps_check_integrity(). For light poisoning and no poisoning, the test will
* check that heap_caps_check_integrity() does not report the corruption.
*/
TEST_CASE("multi_heap poisoning detection", "[heap]")
{
/* malloc some memory to get a pointer */
uint8_t *ptr = heap_caps_malloc(MALLOC_SIZE, MALLOC_CAP_8BIT);

/* free the memory to free the block but keep the pointer in mind */
heap_caps_free(ptr);

/* variable used in the test */
uint8_t original_value = 0x00;

for (size_t i = 0; i < MALLOC_SIZE; i++)
{
/* keep the good value in store in order to check that when we set the byte back
* to its original value, heap_caps_check_integrity() no longer returns the
* heap corruption. */
original_value = ptr[i];

/* set corrupted value in the free memory*/
ptr[i] = CORRUPTED_VALUE;

bool is_heap_ok = heap_caps_check_integrity(MALLOC_CAP_8BIT, true);
#ifdef CONFIG_HEAP_POISONING_COMPREHENSIVE
/* check that heap_caps_check_integrity() detects the corruption */
TEST_ASSERT_FALSE(is_heap_ok);
#else
/* the comprehensive corruption is not checked in the heap_caps_check_integrity() */
TEST_ASSERT_TRUE(is_heap_ok);
#endif
/* fix the corruption by restoring the original value at ptr + i */
ptr[i] = original_value;

/* check that heap_caps_check_integrity() stops reporting the corruption */
is_heap_ok = heap_caps_check_integrity(MALLOC_CAP_8BIT, true);
TEST_ASSERT_TRUE(is_heap_ok);
}
}
2 changes: 1 addition & 1 deletion components/heap/test_multi_heap_host/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ INCLUDE_FLAGS = -I../include -I../../../tools/catch -I../tlsf

GCOV ?= gcov

CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32 -DCONFIG_HEAP_POISONING_COMPREHENSIVE
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32
CFLAGS += -Wall -Werror -fprofile-arcs -ftest-coverage
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32
Expand Down
64 changes: 64 additions & 0 deletions components/heap/test_multi_heap_host/test_multi_heap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "multi_heap.h"

#include "../multi_heap_config.h"
#include "../tlsf/tlsf_common.h"
#include "../tlsf/tlsf_block_functions.h"

#include <string.h>
#include <assert.h>
Expand Down Expand Up @@ -523,3 +525,65 @@ TEST_CASE("multi_heap allocation overhead", "[multi_heap]")

multi_heap_free(heap, x);
}

/* This test will corrupt the memory of a free block in the heap and check
* that in the case of comprehensive poisoning the heap corruption is detected
* by multi_heap_check(). For light poisoning and no poisoning, the test will
* check that multi_heap_check() does not report the corruption.
*/
TEST_CASE("multi_heap poisoning detection", "[multi_heap]")
{
const size_t HEAP_SIZE = 4 * 1024;

/* define heap related data */
uint8_t heap_mem[HEAP_SIZE];
memset(heap_mem, 0x00, HEAP_SIZE);

/* register the heap memory. One free block only will be available */
multi_heap_handle_t heap = multi_heap_register(heap_mem, HEAP_SIZE);

/* offset in memory at which to find the first free memory byte */
const size_t free_memory_offset = sizeof(multi_heap_info_t) + sizeof(control_t) + block_header_overhead;

/* block header of the free block under test in the heap () */
const block_header_t* block = (block_header_t*)(heap_mem + free_memory_offset - sizeof(block_header_t));

/* actual number of bytes potentially filled with the free pattern in the free block under test */
const size_t effective_free_size = block_size(block) - block_header_overhead - offsetof(block_header_t, next_free);

/* variable used in the test */
size_t affected_byte = 0x00;
uint8_t original_value = 0x00;
uint8_t corrupted_value = 0x00;

/* repeat the corruption a few times to cover more of the free memory */
for (size_t i = 0; i < effective_free_size; i++)
{
/* corrupt random bytes in the heap (it needs to be bytes from free memory in
* order to check that the comprehensive poisoning is doing its job) */
affected_byte = free_memory_offset + i;
corrupted_value = (rand() % UINT8_MAX) | 1;

/* keep the good value in store in order to check that when we set the byte back
* to its original value, multi_heap_check() no longer returns the heap corruption. */
original_value = heap_mem[affected_byte];

/* make sure we are not replacing the original value with the same value */
heap_mem[affected_byte] ^= corrupted_value;

bool is_heap_ok = multi_heap_check(heap, true);
#ifdef CONFIG_HEAP_POISONING_COMPREHENSIVE
/* check that multi_heap_check() detects the corruption */
REQUIRE(is_heap_ok == false);
#else
/* the comprehensive corruption is not checked in the multi_heap_check() */
REQUIRE(is_heap_ok == true);
#endif
/* fix the corruption */
heap_mem[affected_byte] = original_value;

/* check that multi_heap_check() stops reporting the corruption */
is_heap_ok = multi_heap_check(heap, true);
REQUIRE(is_heap_ok == true);
}
}

0 comments on commit 4571e19

Please sign in to comment.