On Tue, 8 Jul 2025 at 13:31, Tomasz Kaminski <tkami...@redhat.com> wrote:
>
>
>
> On Sat, Jul 5, 2025 at 1:12 AM Jonathan Wakely <jwak...@redhat.com> wrote:
>>
>> 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.
>>
>> 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.
>> ---
>>
>> Tested x86_64-linux.
>
> I have expressed my preference of doing this check inside the 
> memory_resouce::allocate.

We can still do that separately. The point of this patch is to fix the
misaligned allocations from the pool resources, not improve the EB vs
UB position for the memory_resource::allocate API.

> I know that this throws any potential detection of misaligned resources under 
> the bus,
> however I think there is more benefit of not exposing custom resources to 
> strange alignment values,
> and using the wrong pool.
>
> On the standard level, I think we should make behavior erroneous here, with 
> defined behavior being bit_ceil.

Yeah, I think that's a good idea.


>>
>>
>>  libstdc++-v3/src/c++17/memory_resource.cc     | 26 +++++++--
>>  .../synchronized_pool_resource/118681.cc      |  5 ++
>>  .../unsynchronized_pool_resource/118681.cc    | 58 +++++++++++++++++++
>>  3 files changed, 85 insertions(+), 4 deletions(-)
>>  create mode 100644 
>> libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc
>>  create mode 100644 
>> libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc
>>
>> diff --git a/libstdc++-v3/src/c++17/memory_resource.cc 
>> b/libstdc++-v3/src/c++17/memory_resource.cc
>> index fac4c782c5f7..fddfe2c7dd98 100644
>> --- a/libstdc++-v3/src/c++17/memory_resource.cc
>> +++ b/libstdc++-v3/src/c++17/memory_resource.cc
>> @@ -1242,12 +1242,30 @@ namespace pmr
>>      return pools;
>>    }
>>
>> +  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();
>> +  }
>> +
>>    // Override for memory_resource::do_allocate
>>    void*
>>    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 +1312,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 +1471,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 +1488,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..6d7434ff9106
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc
>> @@ -0,0 +1,5 @@
>> +// { dg-do run { target c++17 } }
>> +// 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..87e1b1d94043
>> --- /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(p2, 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 );
>> +}
>> --
>> 2.50.0
>>

Reply via email to