From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id D746B3858C5F; Mon, 18 Mar 2024 14:04:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D746B3858C5F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1710770679; bh=veYBb8a1YYozdaSenpQ0ExNQmCZRsYuObdTV9xJ7hMk=; h=From:To:Subject:Date:From; b=dPuL8RyEbFhlDIFZFapmftNpTiDtBJBsz4gLoN6WkiE48oo0fR+RxM6iTq7NtXIny 4I+NPRGv/dfTf9Gw3ug3hScn015wrydETk+ygSO2B/Lk4B1qCHu4LLs9TSwHCRwncf A5fI7CyxbTwpE9AfBIxrJ7amPmE3/S8TgjbYwWnE= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r12-10236] libstdc++: Fix std::basic_string::resize_and_overwrite X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/releases/gcc-12 X-Git-Oldrev: d063387b783f49f0d7868259424e7e548ad1e3c2 X-Git-Newrev: b9d09c7cfcaeb777c5cbc2848a84142a329992c9 Message-Id: <20240318140439.D746B3858C5F@sourceware.org> Date: Mon, 18 Mar 2024 14:04:39 +0000 (GMT) List-Id: https://gcc.gnu.org/g:b9d09c7cfcaeb777c5cbc2848a84142a329992c9 commit r12-10236-gb9d09c7cfcaeb777c5cbc2848a84142a329992c9 Author: Jonathan Wakely Date: Tue Aug 15 13:48:23 2023 +0100 libstdc++: Fix std::basic_string::resize_and_overwrite The callable used for resize_and_overwrite was being passed the string's expanded capacity, which might be greater than the new size being requested. This is not conforming, as the standard requires the same n to be passed to the callable that the user passed to resize_and_overwrite. The existing tests didn't catch this because they all used a value which was more than twice the existing capacity, so the _M_create call allocated exactly what was requested, and the value passed to the callable was correct. But when the requested size is greater than the current capacity but smaller than twice the current capacity, _M_create will allocate twice the current capacity and then that value was being passed to the callable. I noticed this because std::format(L"{}", 0.25) was producing L"0.25XX" where the XX characters were whatever happened to be on the stack before the call. When std::format used resize_and_overwrite to widen a string it was copying too many characters into the destination and setting the result's length too long. libstdc++-v3/ChangeLog: * include/bits/basic_string.tcc (resize_and_overwrite): Invoke the callable with the same size as resize_and_overwrite was called with. * testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc: Check with small values for the new size. (cherry picked from commit 4a2b262597e4a6bc5732d4564673c1e19381dcfa) Diff: --- libstdc++-v3/include/bits/basic_string.tcc | 7 ++++--- .../capacity/char/resize_and_overwrite.cc | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc index f3911611aea..7d76cfde172 100644 --- a/libstdc++-v3/include/bits/basic_string.tcc +++ b/libstdc++-v3/include/bits/basic_string.tcc @@ -570,13 +570,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr void basic_string<_CharT, _Traits, _Alloc>:: - resize_and_overwrite(size_type __n, _Operation __op) + resize_and_overwrite(const size_type __n, _Operation __op) { const size_type __capacity = capacity(); _CharT* __p; if (__n > __capacity) { - __p = _M_create(__n, __capacity); + auto __new_capacity = __n; // Must not allow _M_create to modify __n. + __p = _M_create(__new_capacity, __capacity); this->_S_copy(__p, _M_data(), length()); // exclude trailing null #if __cpp_lib_is_constant_evaluated if (std::is_constant_evaluated()) @@ -584,7 +585,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif _M_dispose(); _M_data(__p); - _M_capacity(__n); + _M_capacity(__new_capacity); } else __p = _M_data(); diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc index f716030dad7..0ea5e2b10ef 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/resize_and_overwrite.cc @@ -120,6 +120,26 @@ test05() return true; } +void +test06() +{ + std::string s = "0123456789"; + s.resize_and_overwrite(16, [](char* p, int n) { + VERIFY( n == 16 ); + std::char_traits::copy(p + 10, "0123456798", 6); + return n; + }); + VERIFY( s.size() == 16 ); + VERIFY( s == "0123456789012345" ); + + s.resize_and_overwrite(4, [](char* p, int n) { + VERIFY( n == 4 ); + std::char_traits::copy(p, "abcd", 4); + return n; + }); + VERIFY( s.size() == 4 ); +} + int main() { test01(); @@ -127,4 +147,5 @@ int main() test03(); test04(); static_assert( test05() ); + test06(); }