https://gcc.gnu.org/g:8ba83d3dab23d6b653988b4fd81657c043f0e9fa

commit r17-5130-g8ba83d3dab23d6b653988b4fd81657c043f0e9fa
Author: Nathan Myers <[email protected]>
Date:   Thu Oct 8 19:31:27 2026 -0400

    libstdc++: restore old _M_create behavior [PR127228]
    
    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
    relies on _M_create to allocate no more bytes than requested
    when the old capacity is zero, 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.
            * testsuite/21_strings/basic_string/127228.cc: New test.

Diff:
---
 libstdc++-v3/include/bits/basic_string.tcc         | 12 ++++++++---
 .../testsuite/21_strings/basic_string/127228.cc    | 25 ++++++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/libstdc++-v3/include/bits/basic_string.tcc 
b/libstdc++-v3/include/bits/basic_string.tcc
index 64c99fbb9d64..de82e9978888 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 000000000000..a0517c320f18
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/127228.cc
@@ -0,0 +1,25 @@
+// { dg-do run { target c++23 } }
+// { dg-options "-fno-access-control -fno-inline -fno-ipa-sra -fno-ipa-modref 
-fno-ipa-cp -fno-ipa-icf" }
+
+// libstdc++/127228
+// Object files built against older libstdc++ still call _M_create
+// and rely on it to allocate and report allocating no more storage
+// than was asked for when the old capacity is 0.
+
+#include <string>
+#include <testsuite_hooks.h>
+
+int main()
+{
+  std::string s;
+  {
+    std::string::size_type new_cap = 25, old_cap = 0;
+    (void) s._M_create(new_cap, old_cap);
+    VERIFY(new_cap == 25);
+  }
+  {
+    std::string::size_type new_cap = 2, old_cap = 0;
+    (void) s._M_create(new_cap, old_cap);
+    VERIFY(new_cap == 2);
+  }
+}

Reply via email to