allocators: Apply manual ASan poisoning to PoolResource #32581

pull dergoegge wants to merge 1 commits into bitcoin:master from dergoegge:2025-05-pool-poison changing 3 files +32 βˆ’1
  1. dergoegge commented at 5:18 pm on May 21, 2025: member

    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:

     0diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
     1index c46144b34b..aa6ca15ce1 100644
     2--- a/src/test/coins_tests.cpp
     3+++ b/src/test/coins_tests.cpp
     4@@ -508,6 +508,17 @@ BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest)
     5     BOOST_CHECK(spent_a_duplicate_coinbase);
     6 }
     7
     8+BOOST_AUTO_TEST_CASE(asan_uaf)
     9+{
    10+    CCoinsMapMemoryResource cache_coins_memory_resource{};
    11+    CCoinsMap map(0, SaltedOutpointHasher(/*deterministic=*/true), CCoinsMap::key_equal{}, &cache_coins_memory_resource);
    12+    COutPoint outpoint{};
    13+    map.emplace(outpoint, Coin{});
    14+    auto& coin = map.at(outpoint);
    15+    map.erase(outpoint);
    16+    coin.coin.nHeight = 1;
    17+}
    18+
    19 BOOST_AUTO_TEST_CASE(ccoins_serialization)
    20 {
    21     // Good example
    

    Fix this by applying manual ASan poisoning 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:

    0$ ./build_unit/bin/test_bitcoin --run_test="coins_tests/asan_uaf"
    1Running 1 test case...
    2=================================================================
    3==366064==ERROR: AddressSanitizer: use-after-poison on address 0x7f99c3204870 at pc 0x55569dab6f8a bp 0x7ffe0210e4d0 sp 0x7ffe0210e4c8
    4READ of size 4 at 0x7f99c3204870 thread T0 (b-test)
    
  2. DrahtBot commented at 5:18 pm on May 21, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32581.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK sipa, fanquake, l0rinc

    If your review is incorrectly listed, please react with πŸ‘Ž to this comment and the bot will ignore it on the next update.

  3. dergoegge marked this as a draft on May 21, 2025
  4. dergoegge commented at 5:37 pm on May 21, 2025: member
    Draft until I sort out the CI failures
  5. DrahtBot added the label CI failed on May 21, 2025
  6. DrahtBot commented at 9:30 pm on May 21, 2025: contributor

    🚧 At least one of the CI tasks failed. Task tidy: https://github.com/bitcoin/bitcoin/runs/42652441080 LLM reason (✨ experimental): The CI failure is due to the use of undeclared identifiers related to ASAN in the pool.h file, resulting in compilation errors.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  7. sipa commented at 9:32 pm on May 21, 2025: member
    Concept ACK
  8. dergoegge force-pushed on May 22, 2025
  9. dergoegge force-pushed on May 22, 2025
  10. dergoegge marked this as ready for review on May 22, 2025
  11. l0rinc commented at 8:30 am on May 22, 2025: contributor

    This is because ASan is only aware of the memory chunks allocated by PoolResource but not the individual “sub-chunks” within

    What’s the deeper explanation for this? Can we refactor the code or patch the sanitizers instead of annotating our code in places where we already know the code is problematic? Don’t have a lot of experience in this area, I might be off, was just wondering if there’s a more general solution.

  12. fanquake commented at 8:38 am on May 22, 2025: member
    Concept ACK
  13. dergoegge commented at 9:03 am on May 22, 2025: member

    What’s the depper explanation for this?

    At a high level PoolResource allocates larger chunks of memory (by default 262144 bytes per chunk) and allows for smaller memory sections within those chunks to be allocated (in a way that is optimized for node based containers, e.g. std::unordered_map).

    ASan instruments calls to e.g. malloc/free and new/delete to detect UaF and other issues, so for PoolResource it’ll be aware of the allocations of the larger chunks. However, it does not instrument our custom allocate/deallocate functions and that is likely also not practical to do in a general way, because the exact instrumentation needed depends heavily on the allocator implementation.

    In our case, PoolResource uses the memory allocated for the chunks for both allocating smaller sections as well as its internal free list. That is why if you look at the patch here, internally unpoisoning the free list pointers is necessary before accessing them. Every custom allocator implementation might have different implementation quirks like this and it does not seem feasible for ASan to figure this out on its own.

  14. DrahtBot removed the label CI failed on May 22, 2025
  15. l0rinc commented at 9:27 am on May 22, 2025: contributor
    Thanks for clarifying. Though https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning seems quite old (2015), a recent https://blog.trailofbits.com/2024/05/16/understanding-addresssanitizer-better-memory-safety-for-your-code article also confirms Incorrect access to memory managed by a custom allocator won’t raise an error unless the allocator performs annotations. The sanitizer description also mentioned thread safety warnings and alignment restrictions - but I think we should be okay in both cases here. Concept ACK
  16. dergoegge force-pushed on May 23, 2025
  17. [allocators] Apply manual ASan poisoning to PoolResource ad132761fc
  18. dergoegge force-pushed on May 23, 2025
  19. DrahtBot added the label CI failed on May 23, 2025
  20. DrahtBot commented at 9:10 am on May 23, 2025: contributor

    🚧 At least one of the CI tasks failed. Task fuzzer,address,undefined,integer, no depends: https://github.com/bitcoin/bitcoin/runs/42771108504 LLM reason (✨ experimental): The CI failure is due to build errors caused by macro redefinitions of ASAN_POISON_MEMORY_REGION and ASAN_UNPOISON_MEMORY_REGION.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  21. DrahtBot removed the label CI failed on May 23, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-05-25 18:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me