On Tue, 13 Oct 2026 at 11:29 +0100, Jonathan Wakely wrote:
On Mon, 12 Oct 2026 at 09:22 -0400, Nathan Myers wrote: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 isIt's not a bug, see below.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_PR127228This macro is not in the _GLIBCXX_ namespace.+ size_type __expose_old_M_create(size_type __sz)Do we need a separate function for this? Why not just make _M_create public when _GLIBCXX_TEST_PR127288 is defined? Or better yet, compile the test with -fno-access-control via a dg-options directive. Then you don't need any new function or special macro.
And maybe prevent inlining so we know it's calling the exported
function from the shared lib.
// { dg-options "-fno-access-control -fno-inline -fno-ipa" }
+ { 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.Why "incorrectly"? That was the function's interface. The two calls to _M_create(x, size_type(0)) are relying on that behaviour, because that was the function's behaviour. I think it's only the calls with 0 as the second argument that rely on this, so the commit msg and this comment could be more precise (and lose the judgment that they were incorrect and had a latent bug - the code was working exactly as intended).+ +#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);Please test calling s._M_create(25, size_type(0)) since that's the exact form that the affected callers use. I'd also like to see a check with a new capacity that's less than the size of the SSO buffer. We should verify that _M_create(2, 0) and _M_create(25, 0) both work correctly.+} -- 2.55.0
