Merge bitcoin/bitcoin#32581: allocators: Apply manual ASan poisoning to PoolResource

ad132761fc [allocators] Apply manual ASan poisoning to PoolResource (dergoegge)

Pull request description:

  Currently ASan will not detect use-after-free issues for memory allocated by a `PoolResource`. This is because ASan is only aware of the memory chunks allocated by `PoolResource` but not the individual "sub-chunks" within.

  E.g. this test will not produce an ASan error even though the referenced coin has been deallocated:

  ```c++
  diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
  index c46144b34b..aa6ca15ce1 100644
  --- a/src/test/coins_tests.cpp
  +++ b/src/test/coins_tests.cpp
  @@ -508,6 +508,17 @@ BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest)
       BOOST_CHECK(spent_a_duplicate_coinbase);
   }

  +BOOST_AUTO_TEST_CASE(asan_uaf)
  +{
  +    CCoinsMapMemoryResource cache_coins_memory_resource{};
  +    CCoinsMap map(0, SaltedOutpointHasher(/*deterministic=*/true), CCoinsMap::key_equal{}, &cache_coins_memory_resource);
  +    COutPoint outpoint{};
  +    map.emplace(outpoint, Coin{});
  +    auto& coin = map.at(outpoint);
  +    map.erase(outpoint);
  +    coin.coin.nHeight = 1;
  +}
  +
   BOOST_AUTO_TEST_CASE(ccoins_serialization)
   {
       // Good example
  ```

  Fix this by applying [manual ASan poisoning](https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning) for memory allocated by `PoolResource`:

  * Newly allocated chunks are poisoned as a whole
  * "Sub-chunks" are unpoisoned/re-poisoned during allocation/deallocation

  With the poisoning applied, ASan catches the issue in the test above:
  ```
  $ ./build_unit/bin/test_bitcoin --run_test="coins_tests/asan_uaf"
  Running 1 test case...
  =================================================================
  ==366064==ERROR: AddressSanitizer: use-after-poison on address 0x7f99c3204870 at pc 0x55569dab6f8a bp 0x7ffe0210e4d0 sp 0x7ffe0210e4c8
  READ of size 4 at 0x7f99c3204870 thread T0 (b-test)
  ```

ACKs for top commit:
  achow101:
    ACK ad132761fc
  marcofleon:
    code review ACK ad132761fc

Tree-SHA512: eb5e80bfa9509225e784151807bd8aa21fb0826ca1781dfe81b1d60bd3766019384ea3f9cb8e53398fde2f4e994a9c201b5a9962b4d279d7e52bb60e8961be11
This commit is contained in:
merge-script
2025-08-05 10:47:01 +01:00
3 changed files with 32 additions and 1 deletions

View File

@@ -6,6 +6,7 @@
#define BITCOIN_TEST_UTIL_POOLRESOURCETESTER_H
#include <support/allocators/pool.h>
#include <util/check.h>
#include <algorithm>
#include <cassert>
@@ -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<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
ptr = ptr->m_next;
ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::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<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
ptr = ptr->m_next;
ASAN_POISON_MEMORY_REGION(ptr_, sizeof(typename PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES>::ListNode));
}
}
// also add whatever has not yet been used for blocks