acfdad706d8 added a basic_string member _M_create_plus to use
when allocating string storage, but retained the old _M_create
symbol so that object files built against older releases would
still find it. However, code built into some old object files has
a latent bug that assumes _M_create will, if the old capacity is
zero, allocate only as many bytes as requested, something it no
longer does.

This patch restores _M_create's exact previous behavior, to
rescue those old object files.

libstdc++-v3/ChangeLog:
        PR libstdc++/127228
        * include/bits/basic_string.tcc (_M_create): Restore old version.
        * include/bits/basic_string.h (__expose_old_M_create):
        Add test instrumentation.
        * testsuite/21_strings/basic_string/127228.cc: New test.
---
 libstdc++-v3/include/bits/basic_string.h      |  5 +++++
 libstdc++-v3/include/bits/basic_string.tcc    | 12 ++++++++---
 .../21_strings/basic_string/127228.cc         | 20 +++++++++++++++++++
 3 files changed, 34 insertions(+), 3 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string/127228.cc

diff --git a/libstdc++-v3/include/bits/basic_string.h 
b/libstdc++-v3/include/bits/basic_string.h
index fd9419220a4..ba9a35e7ffd 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -134,6 +134,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       typedef const_iterator __const_iterator;
 #endif
 
+#ifdef __TEST_PR127228
+      size_type __expose_old_M_create(size_type __sz)
+      { return _M_create(__sz, 0), __sz; }
+#endif
+
     private:
       // For ABI reasons this must remain, though unused.
       static _GLIBCXX20_CONSTEXPR pointer
diff --git a/libstdc++-v3/include/bits/basic_string.tcc 
b/libstdc++-v3/include/bits/basic_string.tcc
index 64c99fbb9d6..de82e997888 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -171,9 +171,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     basic_string<_CharT, _Traits, _Alloc>::
     _M_create(size_type& __capacity, size_type __old_capacity)
     {
-      _Alloc_result __r = _M_create_plus(__capacity, __old_capacity);
-      __capacity = __r.__count - 1;  // Leave room for NUL.
-      return __r.__ptr;
+      if (__capacity > max_size())
+       std::__throw_length_error(__N("basic_string::_M_create"));
+      if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
+       {
+         __capacity = 2 * __old_capacity;
+         if (__capacity > max_size())
+           __capacity = max_size();
+       }
+      return _S_allocate(_M_get_allocator(), __capacity + 1);
     }
 
   // NB: This is the special case for Input Iterators, used in
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/127228.cc 
b/libstdc++-v3/testsuite/21_strings/basic_string/127228.cc
new file mode 100644
index 00000000000..8738ae41192
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/127228.cc
@@ -0,0 +1,20 @@
+// { dg-do run { target c++23 } }
+
+// libstdc++/127228
+// Object files built against older libstdc++ still call
+// _M_create, and in some cases (incorrectly) rely on it to
+// allocate, and report allocating, no more storage than was
+// asked for.
+
+#define _TEST_PR127228
+#include <string>
+#include <testsuite_hooks.h>
+
+struct T : public std::string
+{ using std::string::__expose_old_M_create; };
+
+int main()
+{
+  T s;
+  VERIFY(s.__expose_old_M_create(25) == 25);
+}
-- 
2.55.0

Reply via email to