From 0bdb6dc079fd26ef1fe1ed4543c568f1d79159b7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 2 Nov 2023 22:44:32 +0100 Subject: [PATCH] Fix memory poisoning with Asan on arbitrary byte boundaries Asan poisons memory with an 8-byte granularity. We want to make sure that the whole specified region is poisoned (our typical use case is a heap-allocated object, and we want to poison the whole object, and we don't care about the bytes after the end of the object and up to the beginning of the next object). So align the start and end of the region to (un)poison to an 8-byte boundary. Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 7 +++++++ tests/include/test/memory.h | 4 ++++ tests/src/test_memory.c | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 6ab240c592..a3d9d4095a 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -332,6 +332,13 @@ metatest_t metatests[] = { { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, { "test_memory_poison_0_0_8", "asan", test_memory_poison }, + { "test_memory_poison_0_7_8", "asan", test_memory_poison }, + { "test_memory_poison_0_0_1", "asan", test_memory_poison }, + { "test_memory_poison_0_1_2", "asan", test_memory_poison }, + { "test_memory_poison_7_0_8", "asan", test_memory_poison }, + { "test_memory_poison_7_7_8", "asan", test_memory_poison }, + { "test_memory_poison_7_0_1", "asan", test_memory_poison }, + { "test_memory_poison_7_1_2", "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 27baf7a1dc..f610a97a45 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -40,6 +40,10 @@ * Poison a memory area so that any attempt to read or write from it will * cause a runtime failure. * + * Depending on the implementation, this may poison a few bytes beyond the + * indicated region, but will never poison a separate object on the heap + * or a separate object with more than the alignment of a long long. + * * The behavior is undefined if any part of the memory area is invalid. * * This is a no-op in builds without a poisoning method. diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index 6b1404bc3c..c277be85ab 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -19,11 +19,27 @@ #endif #if defined(MBEDTLS_TEST_HAVE_ASAN) +static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) +{ + uintptr_t start = (uintptr_t) *p_ptr; + uintptr_t end = start + (uintptr_t) *p_size; + /* ASan can only poison regions with 8-byte alignment, and only poisons a + * region if it's fully within the requested range. We want to poison the + * whole requested region and don't mind a few extra bytes. Therefore, + * align start down to an 8-byte boundary, and end up to an 8-byte + * boundary. */ + start = start & ~(uintptr_t) 7; + end = (end + 7) & ~(uintptr_t) 7; + *p_ptr = (const unsigned char *) start; + *p_size = end - start; +} + void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) { if (size == 0) { return; } + align_for_asan(&ptr, &size); __asan_poison_memory_region(ptr, size); } @@ -32,6 +48,7 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size) if (size == 0) { return; } + align_for_asan(&ptr, &size); __asan_unpoison_memory_region(ptr, size); } #endif /* Asan */