From ad132761fc49c38769c09653a265fdbc3b93eda5 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Wed, 21 May 2025 16:59:12 +0100 Subject: [PATCH] [allocators] Apply manual ASan poisoning to PoolResource --- src/support/allocators/pool.h | 15 ++++++++++++++- src/test/util/poolresourcetester.h | 7 +++++++ src/util/check.h | 11 +++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/support/allocators/pool.h b/src/support/allocators/pool.h index 873e260b651..86b5da5a8a8 100644 --- a/src/support/allocators/pool.h +++ b/src/support/allocators/pool.h @@ -14,6 +14,8 @@ #include #include +#include + /** * A memory resource similar to std::pmr::unsynchronized_pool_resource, but * optimized for node-based containers. It has the following properties: @@ -155,12 +157,15 @@ class PoolResource final // if there is still any available memory left, put it into the freelist. size_t remaining_available_bytes = std::distance(m_available_memory_it, m_available_memory_end); if (0 != remaining_available_bytes) { + ASAN_UNPOISON_MEMORY_REGION(m_available_memory_it, sizeof(ListNode)); PlacementAddToList(m_available_memory_it, m_free_lists[remaining_available_bytes / ELEM_ALIGN_BYTES]); + ASAN_POISON_MEMORY_REGION(m_available_memory_it, sizeof(ListNode)); } void* storage = ::operator new (m_chunk_size_bytes, std::align_val_t{ELEM_ALIGN_BYTES}); m_available_memory_it = new (storage) std::byte[m_chunk_size_bytes]; m_available_memory_end = m_available_memory_it + m_chunk_size_bytes; + ASAN_POISON_MEMORY_REGION(m_available_memory_it, m_chunk_size_bytes); m_allocated_chunks.emplace_back(m_available_memory_it); } @@ -202,6 +207,7 @@ public: for (std::byte* chunk : m_allocated_chunks) { std::destroy(chunk, chunk + m_chunk_size_bytes); ::operator delete ((void*)chunk, std::align_val_t{ELEM_ALIGN_BYTES}); + ASAN_UNPOISON_MEMORY_REGION(chunk, m_chunk_size_bytes); } } @@ -217,7 +223,11 @@ public: // we've already got data in the pool's freelist, unlink one element and return the pointer // to the unlinked memory. Since FreeList is trivially destructible we can just treat it as // uninitialized memory. - return std::exchange(m_free_lists[num_alignments], m_free_lists[num_alignments]->m_next); + ASAN_UNPOISON_MEMORY_REGION(m_free_lists[num_alignments], sizeof(ListNode)); + auto* next{m_free_lists[num_alignments]->m_next}; + ASAN_POISON_MEMORY_REGION(m_free_lists[num_alignments], sizeof(ListNode)); + ASAN_UNPOISON_MEMORY_REGION(m_free_lists[num_alignments], bytes); + return std::exchange(m_free_lists[num_alignments], next); } // freelist is empty: get one allocation from allocated chunk memory. @@ -228,6 +238,7 @@ public: } // Make sure we use the right amount of bytes for that freelist (might be rounded up), + ASAN_UNPOISON_MEMORY_REGION(m_available_memory_it, round_bytes); return std::exchange(m_available_memory_it, m_available_memory_it + round_bytes); } @@ -244,7 +255,9 @@ public: const std::size_t num_alignments = NumElemAlignBytes(bytes); // put the memory block into the linked list. We can placement construct the FreeList // into the memory since we can be sure the alignment is correct. + ASAN_UNPOISON_MEMORY_REGION(p, sizeof(ListNode)); PlacementAddToList(p, m_free_lists[num_alignments]); + ASAN_POISON_MEMORY_REGION(p, std::max(bytes, sizeof(ListNode))); } else { // Can't use the pool => forward deallocation to ::operator delete(). ::operator delete (p, std::align_val_t{alignment}); diff --git a/src/test/util/poolresourcetester.h b/src/test/util/poolresourcetester.h index 93f62eb2a97..7c06980ea39 100644 --- a/src/test/util/poolresourcetester.h +++ b/src/test/util/poolresourcetester.h @@ -6,6 +6,7 @@ #define BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H #include +#include #include #include @@ -48,7 +49,10 @@ public: size_t size = 0; while (ptr != nullptr) { ++size; + const auto* ptr_ = ptr; + ASAN_UNPOISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource::ListNode)); ptr = ptr->m_next; + ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource::ListNode)); } sizes.push_back(size); } @@ -81,7 +85,10 @@ public: auto* ptr = resource.m_free_lists[freelist_idx]; while (ptr != nullptr) { free_blocks.emplace_back(ptr, bytes); + const auto* ptr_ = ptr; + ASAN_UNPOISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource::ListNode)); ptr = ptr->m_next; + ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource::ListNode)); } } // also add whatever has not yet been used for blocks diff --git a/src/util/check.h b/src/util/check.h index f7dea5a3c2e..6548453a97e 100644 --- a/src/util/check.h +++ b/src/util/check.h @@ -126,4 +126,15 @@ constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] con // NOLINTEND(bugprone-lambda-function-name) +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# include +# endif +#endif + +#ifndef ASAN_POISON_MEMORY_REGION +# define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +# define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#endif + #endif // BITCOIN_UTIL_CHECK_H