https://gcc.gnu.org/g:2de64a64b41989682dcdc621fd72fe107fec8b95

commit r15-9951-g2de64a64b41989682dcdc621fd72fe107fec8b95
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Fri Jul 4 16:44:13 2025 +0100

    libstdc++: Ensure pool resources meet alignment requirements [PR118681]
    
    For allocations with size > alignment and size % alignment != 0 we were
    sometimes returning pointers that did not meet the requested aligment.
    For example, allocate(24, 16) would select the pool for 24-byte objects
    and the second allocation from that pool (at offset 24 bytes into the
    pool) is only 8-byte aligned not 16-byte aligned.
    
    The pool resources need to round up the requested allocation size to a
    multiple of the alignment, so that the selected pool will always return
    allocations that meet the alignment requirement.
    
    This backport includes the fixes for the bootstrap error and the tests.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/118681
            * src/c++17/memory_resource.cc (choose_block_size): New
            function.
            (synchronized_pool_resource::do_allocate): Use choose_block_size
            to determine appropriate block size.
            (synchronized_pool_resource::do_deallocate): Likewise
            (unsynchronized_pool_resource::do_allocate): Likewise.
            (unsynchronized_pool_resource::do_deallocate): Likewise
            * testsuite/20_util/synchronized_pool_resource/118681.cc: New
            test.
            * testsuite/20_util/unsynchronized_pool_resource/118681.cc: New
            test.
    
    Reviewed-by: Tomasz KamiƄski <tkami...@redhat.com>
    
    (cherry picked from commit ac2fb60a67d6d1de6446c25c5623b8a1389f4770)

Diff:
---
 libstdc++-v3/src/c++17/memory_resource.cc          | 56 ++++++++++++++++-----
 .../20_util/synchronized_pool_resource/118681.cc   |  6 +++
 .../20_util/unsynchronized_pool_resource/118681.cc | 58 ++++++++++++++++++++++
 3 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/libstdc++-v3/src/c++17/memory_resource.cc 
b/libstdc++-v3/src/c++17/memory_resource.cc
index fac4c782c5f7..c61569f249ad 100644
--- a/libstdc++-v3/src/c++17/memory_resource.cc
+++ b/libstdc++-v3/src/c++17/memory_resource.cc
@@ -182,8 +182,8 @@ namespace pmr
   // versions will not use this symbol.
   monotonic_buffer_resource::~monotonic_buffer_resource() { release(); }
 
-  namespace {
-
+namespace
+{
   // aligned_size<N> stores the size and alignment of a memory allocation.
   // The size must be a multiple of N, leaving the low log2(N) bits free
   // to store the base-2 logarithm of the alignment.
@@ -221,7 +221,7 @@ namespace pmr
     return (n + alignment - 1) & ~(alignment - 1);
   }
 
-  } // namespace
+} // namespace
 
   // Memory allocated by the upstream resource is managed in a linked list
   // of _Chunk objects. A _Chunk object recording the size and alignment of
@@ -307,8 +307,8 @@ namespace pmr
 
   // Helper types for synchronized_pool_resource & unsynchronized_pool_resource
 
-  namespace {
-
+namespace
+{
   // Simple bitset with runtime size.
   // Tracks which blocks in a pool chunk are used/unused.
   struct bitset
@@ -636,7 +636,7 @@ namespace pmr
 
   static_assert(sizeof(big_block) == (2 * sizeof(void*)));
 
-  } // namespace
+} // namespace
 
   // A pool that serves blocks of a particular size.
   // Each pool manages a number of chunks.
@@ -868,7 +868,16 @@ namespace pmr
     using big_block::big_block;
   };
 
-  namespace {
+namespace
+{
+  // N.B. it is important that we don't skip any power of two sizes if there
+  // is a non-power of two size between them, e.g. must not have pool sizes
+  // of 24 and 40 without having a pool size of 32. Otherwise an allocation
+  // of 32 bytes with alignment 16 would choose the 40-byte pool which is not
+  // correctly aligned for 16-byte alignment. It would be OK (but suboptimal)
+  // to have no pool of size 32 if we have pool sizes of 16 and 64 and no
+  // non-power of two sizes between those, because the example of (32, 16)
+  // would choose the 64-byte pool, which would be correctly aligned.
 
   constexpr size_t pool_sizes[] = {
       8, 16, 24,
@@ -983,7 +992,7 @@ namespace pmr
   using exclusive_lock = lock_guard<shared_mutex>;
 #endif
 
-  } // namespace
+} // namespace
 
   __pool_resource::
   __pool_resource(const pool_options& opts, memory_resource* upstream)
@@ -1075,12 +1084,33 @@ namespace pmr
     return p;
   }
 
+  // Determine the appropriate allocation size, rounding up to a multiple
+  // of the alignment if needed.
+  static inline size_t
+  choose_block_size(size_t bytes, size_t alignment)
+  {
+    if (bytes == 0) [[unlikely]]
+      return alignment;
+
+    // Use bit_ceil in case alignment is invalid (i.e. not a power of two).
+    size_t mask = std::__bit_ceil(alignment) - 1;
+    // Round up to a multiple of alignment.
+    size_t block_size = (bytes + mask) & ~mask;
+
+    if (block_size >= bytes) [[likely]]
+      return block_size;
+
+    // Wrapped around to zero, bytes must have been impossibly large.
+    return numeric_limits<size_t>::max();
+  }
+
+
 #ifdef _GLIBCXX_HAS_GTHREADS
   // synchronized_pool_resource members.
 
   /* Notes on implementation and thread safety:
    *
-   * Each synchronized_pool_resource manages an linked list of N+1 _TPools
+   * Each synchronized_pool_resource manages a linked list of N+1 _TPools
    * objects, where N is the number of threads using the pool resource.
    * Each _TPools object has its own set of pools, with their own chunks.
    * The first element of the list, _M_tpools[0], can be used by any thread.
@@ -1247,7 +1277,7 @@ namespace pmr
   synchronized_pool_resource::
   do_allocate(size_t bytes, size_t alignment)
   {
-    const auto block_size = std::max(bytes, alignment);
+    const auto block_size = choose_block_size(bytes, alignment);
     const pool_options opts = _M_impl._M_opts;
     if (block_size <= opts.largest_required_pool_block)
       {
@@ -1294,7 +1324,7 @@ namespace pmr
   synchronized_pool_resource::
   do_deallocate(void* p, size_t bytes, size_t alignment)
   {
-    size_t block_size = std::max(bytes, alignment);
+    size_t block_size = choose_block_size(bytes, alignment);
     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
       {
        const ptrdiff_t index = pool_index(block_size, _M_impl._M_npools);
@@ -1453,7 +1483,7 @@ namespace pmr
   void*
   unsynchronized_pool_resource::do_allocate(size_t bytes, size_t alignment)
   {
-    const auto block_size = std::max(bytes, alignment);
+    const auto block_size = choose_block_size(bytes, alignment);
     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
       {
        // Recreate pools if release() has been called:
@@ -1470,7 +1500,7 @@ namespace pmr
   unsynchronized_pool_resource::
   do_deallocate(void* p, size_t bytes, size_t alignment)
   {
-    size_t block_size = std::max(bytes, alignment);
+    size_t block_size = choose_block_size(bytes, alignment);
     if (block_size <= _M_impl._M_opts.largest_required_pool_block)
       {
        if (auto pool = _M_find_pool(block_size))
diff --git 
a/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc 
b/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc
new file mode 100644
index 000000000000..facbf00400c9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc
@@ -0,0 +1,6 @@
+// { dg-do run { target c++17 } }
+// { dg-require-gthreads "" }
+// Bug 118681 - unsynchronized_pool_resource may fail to respect alignment
+
+#define RESOURCE std::pmr::synchronized_pool_resource
+#include "../unsynchronized_pool_resource/118681.cc"
diff --git 
a/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc 
b/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc
new file mode 100644
index 000000000000..9935f793cf91
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc
@@ -0,0 +1,58 @@
+// { dg-do run { target c++17 } }
+// Bug 118681 - unsynchronized_pool_resource may fail to respect alignment
+
+#include <memory_resource>
+#include <cstdio>
+#include <testsuite_hooks.h>
+
+#ifndef RESOURCE
+# define RESOURCE std::pmr::unsynchronized_pool_resource
+#endif
+
+bool any_misaligned = false;
+
+bool
+is_aligned(void* p, [[maybe_unused]] std::size_t size, std::size_t alignment)
+{
+  const bool misaligned = reinterpret_cast<std::uintptr_t>(p) % alignment;
+#ifdef DEBUG
+  std::printf("allocate(%2zu, %2zu): %p is aligned %scorrectly\n",
+             size, alignment, p, misaligned ? "in" : "");
+  any_misaligned |= misaligned;
+  return true;
+#endif
+  return ! misaligned;
+}
+
+void
+test_alignment(std::pmr::memory_resource& res, bool dealloc)
+{
+  for (std::size_t alignment : { 8, 16, 32, 64 })
+  {
+    for (std::size_t size : { 9, 12, 24, 40, 48, 56, 72 })
+    {
+      void* p1 = res.allocate(size, alignment);
+      void* p2 = res.allocate(size, alignment);
+
+      VERIFY( is_aligned(p1, size, alignment) );
+      VERIFY( is_aligned(p2, size, alignment) );
+
+      if (dealloc)
+      {
+       res.deallocate(p1, size, alignment);
+       res.deallocate(p2, size, alignment);
+      }
+    }
+  }
+}
+
+int main()
+{
+  RESOURCE res;
+  test_alignment(res, true);
+  res.release();
+  test_alignment(res, false);
+  res.release();
+
+  VERIFY( ! any_misaligned );
+}

Reply via email to