https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8670

--- Comment #22 from Jonathan Wakely <redi at gcc dot gnu.org> ---
This bug is still present in the COW std::string, which is still supported even
though it's not the default.

There are two problems. The first is the one reported by James Kanze, that the
string contents need to be aligned to alignof(_CharT) but are currently aligned
to 1. The second, stated by Nathan in comment 13, is that the _Rep object needs
to be aligned to alignof(_Rep), not 1. The reference count in the _Rep ends up
misaligned, and the atomic operations on it are undefined.

Here's a C++17 test case that shows both problems:

#define _GLIBCXX_USE_CXX11_ABI 0

#include <string>

template<typename T>
struct alloc
{
  using value_type = T;

  alloc() = default;

  template<typename U>
    alloc(const alloc<U>&) { }

  T* allocate(std::size_t n)
  {
    if constexpr (std::is_same_v<T, char>)
      return next.allocate(n + 1) + 1;
    return next.allocate(n);
  }

  void deallocate(T* p, std::size_t n)
  {
    if constexpr (std::is_same_v<T, char>)
      return next.deallocate(p - 1, n + 1);
    return next.deallocate(p, n);
  }

  [[no_unique_address]] std::allocator<T> next;

  bool operator==(const alloc<T>&) const { return true; }
};

template<typename C>
  using String = std::basic_string<C, std::char_traits<C>, alloc<C>>;

int main()
{
  String<long double> sd(2, 0.0L);
  return sd[1];
}



This results in loads of UBsan errors like:

/usr/include/c++/13/bits/cow_string.h:3604:24: runtime error: member access
within misaligned address 0x0000006f22b1 for type 'struct _Rep', which requires
8 byte alignment
0x0000006f22b1: note: pointer points here
 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00
00 00  00 00 00 00 00
              ^ 
...
/usr/include/c++/13/bits/char_traits.h:307:15: runtime error: store to
misaligned address 0x0000006f22c9 for type 'char_type', which requires 16 byte
alignment
0x0000006f22c9: note: pointer points here
 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00
00 00  00 00 00 00 00
              ^ 
...
/usr/include/c++/13/bits/cow_string.h:252:46: runtime error: reference binding
to misaligned address 0x0000006f22e9 for type 'char_type', which requires 16
byte alignment
0x0000006f22e9: note: pointer points here
 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00
00 00  00 00 00 00 00
              ^ 
...
/usr/include/c++/13/ext/atomicity.h:84:18: runtime error: load of misaligned
address 0x0000006f22c1 for type '_Atomic_word', which requires 4 byte alignment
0x0000006f22c1: note: pointer points here
 00 00 00  00 ff ff ff ff 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00
00 00  00 00 00 00 00
              ^ 


It seems to me that we can just do:

      struct __attribute__((__aligned__(__alignof__(_CharT)))) _Rep_base
      {
        size_type               _M_length;
        size_type               _M_capacity;
        _Atomic_word            _M_refcount;
      };

And then stop allocating raw bytes (with alignment 1) to place the _Rep into,
and use this allocator type instead:

        typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
          rebind<_Rep>::other _Rep_alloc;

Then of course we need to adjust the size that we (de)allocate, to be in units
of sizeof(_Rep) not bytes.

Reply via email to