From 8ad049e629a41a73d143ed047940cd409e5f1c4e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 21 Jun 2023 16:09:11 -0700 Subject: [PATCH 1/2] Revert "Utilize `allocate_at_least` (#3712)" This reverts commit e37227e58514eed5fbcc83de205aec658cc11cfc. This broke BitCoin in Microsoft's internal Real World Code (RWC) test suite. They publicy derive an allocator from `std::allocate`, implementing `allocate` and `deallocate` but not `allocate_at_least` (https://github.com/bitcoin/bitcoin/blob/f1b4975461364d5d40d2bfafc6b165dd5d7eec30/src/support/allocators/secure.h#L19-L56). When `vector` allocates memory with `std::allocator::allocate_at_least` and tries to free it with `secure_allocator::deallocate`, terrible things happen. We suspect this pattern is widespread, so we're reverting the change for now. --- stl/inc/deque | 5 +- stl/inc/sstream | 9 +- stl/inc/syncstream | 13 +-- stl/inc/vector | 60 +++++----- stl/inc/xmemory | 12 -- stl/inc/xstring | 63 ++++------- tests/std/test.lst | 1 - .../tests/GH_003570_allocate_at_least/env.lst | 4 - .../GH_003570_allocate_at_least/test.cpp | 104 ------------------ 9 files changed, 60 insertions(+), 211 deletions(-) delete mode 100644 tests/std/tests/GH_003570_allocate_at_least/env.lst delete mode 100644 tests/std/tests/GH_003570_allocate_at_least/test.cpp diff --git a/stl/inc/deque b/stl/inc/deque index 2eb29ec475..74586f9dc3 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -1557,13 +1557,12 @@ private: _Newsize *= 2; } + _Count = _Newsize - _Mapsize(); size_type _Myboff = _Myoff() / _Block_size; - _Mapptr _Newmap = _Allocate_at_least_helper(_Almap, _Newsize); + _Mapptr _Newmap = _Almap.allocate(_Mapsize() + _Count); _Mapptr _Myptr = _Newmap + _Myboff; - _Count = _Newsize - _Mapsize(); - _Myptr = _STD uninitialized_copy(_Map() + _Myboff, _Map() + _Mapsize(), _Myptr); // copy initial to end if (_Myboff <= _Count) { // increment greater than offset of initial block _Myptr = _STD uninitialized_copy(_Map(), _Map() + _Myboff, _Myptr); // copy rest of old diff --git a/stl/inc/sstream b/stl/inc/sstream index 222dd02579..15584a8085 100644 --- a/stl/inc/sstream +++ b/stl/inc/sstream @@ -261,7 +261,7 @@ protected: return _Traits::eof(); } - const auto _Newptr = _Unfancy(_Allocate_at_least_helper(_Al, _Newsize)); + const auto _Newptr = _Unfancy(_Al.allocate(_Newsize)); _Traits::copy(_Newptr, _Oldptr, _Oldsize); const auto _New_pnext = _Newptr + _Oldsize; @@ -430,7 +430,7 @@ protected: return pos_type{_Off}; } - void _Init(const _Elem* _Ptr, const _Mysize_type _Count, int _State) { + void _Init(const _Elem* _Ptr, _Mysize_type _Count, int _State) { // initialize buffer to [_Ptr, _Ptr + _Count), set state _State &= ~_From_rvalue; @@ -440,10 +440,9 @@ protected: if (_Count != 0 && (_State & (_Noread | _Constant)) != (_Noread | _Constant)) { // finite buffer that can be read or written, set it up - _Mysize_type _Newsize = _Count; - const auto _Pnew = _Unfancy(_Allocate_at_least_helper(_Al, _Newsize)); + const auto _Pnew = _Unfancy(_Al.allocate(_Count)); _Traits::copy(_Pnew, _Ptr, _Count); - _Seekhigh = _Pnew + _Newsize; + _Seekhigh = _Pnew + _Count; if (!(_State & _Noread)) { _Mysb::setg(_Pnew, _Pnew, _Seekhigh); // setup read buffer diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 969d756a05..ec106eeff8 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -120,10 +120,10 @@ public: if (_Al != _Right_al) { _Tidy(); - _Size_type _Right_buf_size = _Right._Get_buffer_size(); + const _Size_type _Right_buf_size = _Right._Get_buffer_size(); const _Size_type _Right_data_size = _Right._Get_data_size(); - _Elem* const _New_ptr = _Unfancy(_Allocate_at_least_helper(_Al, _Right_buf_size)); + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size)); _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); @@ -217,11 +217,11 @@ protected: return _Traits::eof(); } - _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); + const _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); _Elem* const _Old_ptr = streambuf_type::pbase(); const _Size_type _Old_data_size = _Get_data_size(); - _Elem* const _New_ptr = _Unfancy(_Allocate_at_least_helper(_Al, _New_capacity)); + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); if (0 < _Buf_size) { _Al.deallocate(_Refancy<_Pointer>(_Old_ptr), _Buf_size); @@ -237,9 +237,8 @@ private: static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size void _Init() { - _Size_type _New_capacity = _Min_size; - _Elem* const _New_ptr = _Unfancy(_Allocate_at_least_helper(_Getal(), _New_capacity)); - streambuf_type::setp(_New_ptr, _New_ptr + _New_capacity); + _Elem* const _New_ptr = _Unfancy(_Getal().allocate(_Min_size)); + streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); } void _Tidy() noexcept { diff --git a/stl/inc/vector b/stl/inc/vector index c6552845a3..5b05f4afa4 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -825,10 +825,10 @@ private: _Xlength(); } - const size_type _Newsize = _Oldsize + 1; - size_type _Newcapacity = _Calculate_growth(_Newsize); + const size_type _Newsize = _Oldsize + 1; + const size_type _Newcapacity = _Calculate_growth(_Newsize); - const pointer _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); + const pointer _Newvec = _Al.allocate(_Newcapacity); const pointer _Constructed_last = _Newvec + _Whereoff + 1; pointer _Constructed_first = _Constructed_last; @@ -912,10 +912,10 @@ private: _Xlength(); } - const size_type _Newsize = _Oldsize + _Count; - size_type _Newcapacity = _Calculate_growth(_Newsize); + const size_type _Newsize = _Oldsize + _Count; + const size_type _Newcapacity = _Calculate_growth(_Newsize); - const pointer _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); + const pointer _Newvec = _Al.allocate(_Newcapacity); const pointer _Constructed_last = _Newvec + _Oldsize + _Count; pointer _Constructed_first = _Constructed_last; @@ -1033,10 +1033,10 @@ public: _Xlength(); } - const size_type _Newsize = _Oldsize + _Count; - size_type _Newcapacity = _Calculate_growth(_Newsize); + const size_type _Newsize = _Oldsize + _Count; + const size_type _Newcapacity = _Calculate_growth(_Newsize); - const pointer _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); + const pointer _Newvec = _Al.allocate(_Newcapacity); const pointer _Constructed_last = _Newvec + _Whereoff + _Count; pointer _Constructed_first = _Constructed_last; @@ -1128,10 +1128,10 @@ private: _Xlength(); } - const size_type _Newsize = _Oldsize + _Count; - size_type _Newcapacity = _Calculate_growth(_Newsize); + const size_type _Newsize = _Oldsize + _Count; + const size_type _Newcapacity = _Calculate_growth(_Newsize); - const pointer _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); + const pointer _Newvec = _Al.allocate(_Newcapacity); const auto _Whereoff = static_cast(_Whereptr - _Oldfirst); const pointer _Constructed_last = _Newvec + _Whereoff + _Count; pointer _Constructed_first = _Constructed_last; @@ -1518,10 +1518,10 @@ private: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; - const auto _Oldsize = static_cast(_Mylast - _Myfirst); - size_type _Newcapacity = _Calculate_growth(_Newsize); + const auto _Oldsize = static_cast(_Mylast - _Myfirst); + const size_type _Newcapacity = _Calculate_growth(_Newsize); - const pointer _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); + const pointer _Newvec = _Al.allocate(_Newcapacity); const pointer _Appended_first = _Newvec + _Oldsize; pointer _Appended_last = _Appended_first; @@ -1598,10 +1598,7 @@ public: } private: - enum class _Reallocation_policy { _At_least, _Exactly }; - - template <_Reallocation_policy _Policy> - _CONSTEXPR20 void _Reallocate(size_type& _Newcapacity) { + _CONSTEXPR20 void _Reallocate_exactly(const size_type _Newcapacity) { // set capacity to _Newcapacity (without geometric growth), provide strong guarantee auto& _Al = _Getal(); auto& _My_data = _Mypair._Myval2; @@ -1610,13 +1607,7 @@ private: const auto _Size = static_cast(_Mylast - _Myfirst); - pointer _Newvec; - if constexpr (_Policy == _Reallocation_policy::_At_least) { - _Newvec = _Allocate_at_least_helper(_Al, _Newcapacity); - } else { - _STL_INTERNAL_STATIC_ASSERT(_Policy == _Reallocation_policy::_Exactly); - _Newvec = _Al.allocate(_Newcapacity); - } + const pointer _Newvec = _Al.allocate(_Newcapacity); _TRY_BEGIN if constexpr (is_nothrow_move_constructible_v<_Ty> || !is_copy_constructible_v<_Ty>) { @@ -1684,14 +1675,14 @@ private: } public: - _CONSTEXPR20 void reserve(_CRT_GUARDOVERFLOW size_type _Newcapacity) { + _CONSTEXPR20 void reserve(_CRT_GUARDOVERFLOW const size_type _Newcapacity) { // increase capacity to _Newcapacity (without geometric growth), provide strong guarantee if (_Newcapacity > capacity()) { // something to do (reserve() never shrinks) if (_Newcapacity > max_size()) { _Xlength(); } - _Reallocate<_Reallocation_policy::_At_least>(_Newcapacity); + _Reallocate_exactly(_Newcapacity); } } @@ -1703,8 +1694,7 @@ public: if (_Oldfirst == _Oldlast) { _Tidy(); } else { - size_type _Newcapacity = static_cast(_Oldlast - _Oldfirst); - _Reallocate<_Reallocation_policy::_Exactly>(_Newcapacity); + _Reallocate_exactly(static_cast(_Oldlast - _Oldfirst)); } } } @@ -1986,7 +1976,7 @@ private: return _Geometric; // geometric growth is sufficient } - _CONSTEXPR20 void _Buy_raw(size_type _Newcapacity) { + _CONSTEXPR20 void _Buy_raw(const size_type _Newcapacity) { // allocate array with _Newcapacity elements auto& _My_data = _Mypair._Myval2; pointer& _Myfirst = _My_data._Myfirst; @@ -1996,10 +1986,10 @@ private: _STL_INTERNAL_CHECK(!_Myfirst && !_Mylast && !_Myend); // check that *this is tidy _STL_INTERNAL_CHECK(0 < _Newcapacity && _Newcapacity <= max_size()); - const pointer _Newvec = _Allocate_at_least_helper(_Getal(), _Newcapacity); - _Myfirst = _Newvec; - _Mylast = _Newvec; - _Myend = _Newvec + _Newcapacity; + const auto _Newvec = _Getal().allocate(_Newcapacity); + _Myfirst = _Newvec; + _Mylast = _Newvec; + _Myend = _Newvec + _Newcapacity; } _CONSTEXPR20 void _Buy_nonzero(const size_type _Newcapacity) { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index c2c73ef590..6c8992f47a 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2174,18 +2174,6 @@ _NODISCARD constexpr bool _Allocators_equal(const _Alloc& _Lhs, const _Alloc& _R } } -template -_NODISCARD_RAW_PTR_ALLOC _CONSTEXPR20 typename allocator_traits<_Alloc>::pointer _Allocate_at_least_helper( - _Alloc& _Al, _CRT_GUARDOVERFLOW typename allocator_traits<_Alloc>::size_type& _Count) { -#if _HAS_CXX23 - auto [_Ptr, _Allocated] = allocator_traits<_Alloc>::allocate_at_least(_Al, _Count); - _Count = _Allocated; - return _Ptr; -#else // _HAS_CXX23 - return _Al.allocate(_Count); -#endif // _HAS_CXX23 -} - _EXPORT_STD template _NODISCARD_REMOVE_ALG _CONSTEXPR20 _FwdIt remove(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { // remove each matching _Val diff --git a/stl/inc/xstring b/stl/inc/xstring index b034af9425..ed3dd80dea 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -2647,11 +2647,9 @@ private: return; } - _My_data._Myres = _BUF_SIZE - 1; - size_type _New_capacity = _Calculate_growth(_Count); - ++_New_capacity; - const pointer _New_ptr = _Allocate_at_least_helper(_Al, _New_capacity); // throws - --_New_capacity; + _My_data._Myres = _BUF_SIZE - 1; + const size_type _New_capacity = _Calculate_growth(_Count); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); @@ -2693,10 +2691,8 @@ private: } if (_Count >= _BUF_SIZE) { - size_type _New_capacity = _Calculate_growth(_Count); - ++_New_capacity; - const pointer _New_ptr = _Allocate_at_least_helper(_Al, _New_capacity); // throws - --_New_capacity; + const size_type _New_capacity = _Calculate_growth(_Count); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); _My_data._Myres = _New_capacity; @@ -2712,11 +2708,9 @@ private: _Xlen_string(); // result too long } - const auto _Old_ptr = _My_data._Myptr(); - size_type _New_capacity = _Calculate_growth(_My_data._Mysize); - ++_New_capacity; - const pointer _New_ptr = _Allocate_at_least_helper(_Al, _New_capacity); // throws - --_New_capacity; + const auto _Old_ptr = _My_data._Myptr(); + const size_type _New_capacity = _Calculate_growth(_My_data._Mysize); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); _Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize); @@ -2798,11 +2792,9 @@ public: _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws if (_New_capacity < _New_size) { - _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, max_size()); - ++_New_capacity; - const pointer _Fancyptr = _Allocate_at_least_helper(_Getal(), _New_capacity); // throws - --_New_capacity; - _Ptr = _Unfancy(_Fancyptr); + _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, max_size()); + const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws + _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); _Start_element_lifetimes(_Ptr, _New_capacity + 1); @@ -2871,12 +2863,10 @@ public: _Xlen_string(); } - auto _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, _Max); - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); + const auto _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, _Max); + auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws - ++_New_capacity; - const pointer _Fancyptr = _Allocate_at_least_helper(_Getal(), _New_capacity); // throws - --_New_capacity; + const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws // nothrow hereafter _Start_element_lifetimes(_Unfancy(_Fancyptr), _New_capacity + 1); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); @@ -2956,10 +2946,9 @@ public: _Result._Res = _My_data._Myres + 1; } else { // use _BUF_SIZE + 1 to avoid SSO, if the buffer is assigned back - size_type _Allocated = _BUF_SIZE + 1; - _Result._Ptr = _Allocate_at_least_helper(_Al, _Allocated); + _Result._Ptr = _Al.allocate(_BUF_SIZE + 1); _Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE); - _Result._Res = _Allocated; + _Result._Res = _BUF_SIZE + 1; } _My_data._Orphan_all(); _Tidy_init(); @@ -3178,13 +3167,11 @@ public: if (_Right._Mypair._Myval2._Large_string_engaged()) { const auto _New_size = _Right._Mypair._Myval2._Mysize; - auto _New_capacity = _Calculate_growth(_New_size, 0, _Right.max_size()); + const auto _New_capacity = _Calculate_growth(_New_size, 0, _Right.max_size()); auto _Right_al_non_const = _Right_al; - ++_New_capacity; - const auto _New_ptr = _Allocate_at_least_helper(_Right_al_non_const, _New_capacity); // throws - --_New_capacity; + const auto _New_ptr = _Right_al_non_const.allocate(_New_capacity + 1); // throws - _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); + _Start_element_lifetimes(_Unfancy(_New_ptr), _New_size + 1); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_Right._Mypair._Myval2._Bx._Ptr), _New_size + 1); _Tidy_deallocate(); @@ -4749,11 +4736,9 @@ private: } const size_type _Old_capacity = _Mypair._Myval2._Myres; - size_type _New_capacity = _Calculate_growth(_New_size); + const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); - ++_New_capacity; - const pointer _New_ptr = _Allocate_at_least_helper(_Al, _New_capacity); // throws - --_New_capacity; + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); _Mypair._Myval2._Orphan_all(); @@ -4784,11 +4769,9 @@ private: const size_type _New_size = _Old_size + _Size_increase; const size_type _Old_capacity = _My_data._Myres; - size_type _New_capacity = _Calculate_growth(_New_size); + const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); - ++_New_capacity; - const pointer _New_ptr = _Allocate_at_least_helper(_Al, _New_capacity); // throws - --_New_capacity; + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); _My_data._Orphan_all(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 822cf153b8..cfc1a2f4f3 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -222,7 +222,6 @@ tests\GH_003022_substr_allocator tests\GH_003105_piecewise_densities tests\GH_003119_error_category_ctor tests\GH_003246_cmath_narrowing -tests\GH_003570_allocate_at_least tests\GH_003617_vectorized_meow_element tests\GH_003676_format_large_hh_mm_ss_values tests\GH_003735_char_traits_signatures diff --git a/tests/std/tests/GH_003570_allocate_at_least/env.lst b/tests/std/tests/GH_003570_allocate_at_least/env.lst deleted file mode 100644 index 642f530ffa..0000000000 --- a/tests/std/tests/GH_003570_allocate_at_least/env.lst +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/GH_003570_allocate_at_least/test.cpp b/tests/std/tests/GH_003570_allocate_at_least/test.cpp deleted file mode 100644 index 65821ea009..0000000000 --- a/tests/std/tests/GH_003570_allocate_at_least/test.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -struct signaller { - [[nodiscard]] bool consume() { - return exchange(is_set, false); - } - - void set() { - is_set = true; - } - -private: - bool is_set = false; -}; - -signaller allocate_at_least_signal; - -template -struct signalling_allocator { - using value_type = T; - - signalling_allocator() = default; - - template - signalling_allocator(const signalling_allocator&) {} - - T* allocate(size_t count) { - T* const ptr = static_cast(malloc(count * sizeof(T))); - if (ptr) { - return ptr; - } - - throw bad_alloc(); - } - - allocation_result allocate_at_least(size_t count) { - allocate_at_least_signal.set(); - return {allocate(count * 2), count * 2}; - } - - void deallocate(T* ptr, size_t) noexcept { - free(ptr); - } - - friend bool operator==(const signalling_allocator&, const signalling_allocator&) = default; -}; - -template -void test_container() { - T container; - const size_t reserve_count = container.capacity() + 100; - container.reserve(reserve_count); - assert(allocate_at_least_signal.consume()); - assert(container.capacity() >= reserve_count * 2); - assert(container.size() == 0); -} - -void test_deque() { - deque> d; - d.resize(100); - assert(allocate_at_least_signal.consume()); - assert(d.size() == 100); -} - -void test_stream_overflow(auto& stream) { - stream << "my very long string that is indeed very long in order to make sure " - << "that overflow is called, hopefully calling allocate_at_least in return"; - assert(allocate_at_least_signal.consume()); -} - -void test_sstream() { - basic_stringstream, signalling_allocator> ss; - ss.str("my_cool_string"); - assert(allocate_at_least_signal.consume()); - test_stream_overflow(ss); -} - -void test_syncstream() { - basic_syncbuf, signalling_allocator> buf; - basic_osyncstream, signalling_allocator> ss(&buf); - test_stream_overflow(ss); -} - -int main() { - test_deque(); - test_container, signalling_allocator>>(); - test_container>>(); - test_sstream(); - test_syncstream(); -} From 8c1871fdbc9d125b6c6ab8f84826ab2df20b18cd Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 21 Jun 2023 16:20:57 -0700 Subject: [PATCH 2/2] Reapply `basic_string` fix from the reverted `allocate_at_least` change --- stl/inc/xstring | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xstring b/stl/inc/xstring index ed3dd80dea..b5ab9511a7 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -3171,7 +3171,7 @@ public: auto _Right_al_non_const = _Right_al; const auto _New_ptr = _Right_al_non_const.allocate(_New_capacity + 1); // throws - _Start_element_lifetimes(_Unfancy(_New_ptr), _New_size + 1); + _Start_element_lifetimes(_Unfancy(_New_ptr), _New_capacity + 1); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_Right._Mypair._Myval2._Bx._Ptr), _New_size + 1); _Tidy_deallocate();