Ver Fonte

heap: Add test to check that the corruption of free memory is detected

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'.
Guillaume Souchere há 3 anos atrás
pai
commit
4571e19387

+ 64 - 0
components/heap/test/test_corruption_check.c

@@ -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);
+    }
+}

+ 1 - 1
components/heap/test_multi_heap_host/Makefile

@@ -17,7 +17,7 @@ INCLUDE_FLAGS = -I../include -I../../../tools/catch -I../tlsf
 
 
 GCOV ?= gcov
 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
 CFLAGS += -Wall -Werror -fprofile-arcs -ftest-coverage
 CXXFLAGS += -std=c++11 -Wall -Werror  -fprofile-arcs -ftest-coverage
 CXXFLAGS += -std=c++11 -Wall -Werror  -fprofile-arcs -ftest-coverage
 LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32
 LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32

+ 64 - 0
components/heap/test_multi_heap_host/test_multi_heap.cpp

@@ -2,6 +2,8 @@
 #include "multi_heap.h"
 #include "multi_heap.h"
 
 
 #include "../multi_heap_config.h"
 #include "../multi_heap_config.h"
+#include "../tlsf/tlsf_common.h"
+#include "../tlsf/tlsf_block_functions.h"
 
 
 #include <string.h>
 #include <string.h>
 #include <assert.h>
 #include <assert.h>
@@ -523,3 +525,65 @@ TEST_CASE("multi_heap allocation overhead", "[multi_heap]")
 
 
     multi_heap_free(heap, x);
     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);
+    }
+}