diff --git a/programs/test/metatest.c b/programs/test/metatest.c index b8dffa9bbd..6ab240c592 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -32,6 +32,7 @@ #include #include "test/helpers.h" #include "test/macros.h" +#include "test/memory.h" #include #include @@ -143,6 +144,45 @@ void memory_leak(const char *name) /* Leak of a heap object */ } +/* name = "test_memory_poison_%(start)_%(offset)_%(count)" + * Poison a region starting at start from an 8-byte aligned origin, + * encompassing count bytes. Access the region at offset from the start. + */ +void test_memory_poison(const char *name) +{ + size_t start = 0, offset = 0, count = 0; + if (sscanf(name, "%*[^0-9]%zu%*[^0-9]%zu%*[^0-9]%zu", + &start, &offset, &count) != 3) { + mbedtls_fprintf(stderr, "%s: Bad name format: %s\n", __func__, name); + return; + } + + union { + long long ll; + unsigned char buf[32]; + } aligned; + memset(aligned.buf, 'a', sizeof(aligned.buf)); + + if (start > sizeof(aligned.buf)) { + mbedtls_fprintf(stderr, "%s: start=%zu > size=%zu", __func__, + start, sizeof(aligned.buf)); + return; + } + if (start + count > sizeof(aligned.buf)) { + mbedtls_fprintf(stderr, "%s: start+count=%zu > size=%zu", __func__, + start + count, sizeof(aligned.buf)); + return; + } + if (offset >= count) { + mbedtls_fprintf(stderr, "%s: offset=%zu >= count=%zu", __func__, + offset, count); + return; + } + + MBEDTLS_TEST_MEMORY_POISON(aligned.buf + start, count); + mbedtls_printf("%u\n", (unsigned) aligned.buf[start + offset]); +} + /****************************************************************/ /* Threading */ @@ -291,6 +331,7 @@ metatest_t metatests[] = { { "double_free", "asan", double_free }, { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, + { "test_memory_poison_0_0_8", "asan", test_memory_poison }, { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index ef05112f2f..27baf7a1dc 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -15,4 +15,79 @@ #include "mbedtls/build_info.h" #include "mbedtls/platform.h" +/** \def MBEDTLS_TEST_MEMORY_CAN_POISON + * + * This macro is defined if the tests are compiled with a method to mark + * memory as poisoned, which can be used to enforce some memory access + * policies. + * + * Currently, only Asan (Address Sanitizer) is supported. + */ +#if defined(__SANITIZE_ADDRESS__) +# define MBEDTLS_TEST_HAVE_ASAN +#endif +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define MBEDTLS_TEST_HAVE_ASAN +# endif +#endif +#if defined(MBEDTLS_TEST_HAVE_ASAN) +# define MBEDTLS_TEST_MEMORY_CAN_POISON +#endif + +/** \def MBEDTLS_TEST_MEMORY_POISON(buf, size) + * + * Poison a memory area so that any attempt to read or write from it will + * cause a runtime failure. + * + * The behavior is undefined if any part of the memory area is invalid. + * + * This is a no-op in builds without a poisoning method. + * See #MBEDTLS_TEST_MEMORY_CAN_POISON. + * + * \param buf Pointer to the beginning of the memory area to poison. + * \param size Size of the memory area in bytes. + */ + +/** \def MBEDTLS_TEST_MEMORY_UNPOISON(buf, size) + * + * Undo the effect of #MBEDTLS_TEST_MEMORY_POISON. + * + * The behavior is undefined if any part of the memory area is invalid, + * or if the memory area contains a mixture of poisoned and unpoisoned parts. + * + * This is a no-op in builds without a poisoning method. + * See #MBEDTLS_TEST_MEMORY_CAN_POISON. + * + * \param buf Pointer to the beginning of the memory area to unpoison. + * \param size Size of the memory area in bytes. + */ + +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + +/** Poison a memory area so that any attempt to read or write from it will + * cause a runtime failure. + * + * The behavior is undefined if any part of the memory area is invalid. + */ +void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size); +#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) \ + mbedtls_test_memory_poison(ptr, size) + +/** Undo the effect of mbedtls_test_memory_poison(). + * + * This is a no-op if the given area is entirely valid, unpoisoned memory. + * + * The behavior is undefined if any part of the memory area is invalid, + * or if the memory area contains a mixture of poisoned and unpoisoned parts. + */ +void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); +#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) \ + mbedtls_test_memory_unpoison(ptr, size) + +#else /* MBEDTLS_TEST_MEMORY_CAN_POISON */ +#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) ((void) 0) +#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) ((void) 0) +#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ + #endif /* TEST_MEMORY_H */ diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index cda91e5eaf..6b1404bc3c 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -13,3 +13,25 @@ #include #include +#if defined(MBEDTLS_TEST_HAVE_ASAN) +#include +#include +#endif + +#if defined(MBEDTLS_TEST_HAVE_ASAN) +void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) +{ + if (size == 0) { + return; + } + __asan_poison_memory_region(ptr, size); +} + +void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size) +{ + if (size == 0) { + return; + } + __asan_unpoison_memory_region(ptr, size); +} +#endif /* Asan */