buffer source before reallocation in string::append(InputIt, InputIt)#1170
buffer source before reallocation in string::append(InputIt, InputIt)#1170Ramya-9353 wants to merge 2 commits into
Conversation
|
An automated preview of the documentation is available at https://1170.json.prtest2.cppalliance.org/libs/json/doc/html/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-07-17 10:24:25 UTC |
|
GCOVR code coverage report https://1170.json.prtest2.cppalliance.org/gcovr/index.html Build time: 2026-07-17 10:53:46 UTC |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1170 +/- ##
========================================
Coverage 93.91% 93.92%
========================================
Files 91 91
Lines 9265 9279 +14
========================================
+ Hits 8701 8715 +14
Misses 564 564
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
|
|
Thank you for catching this, but I am not sold on this implementation. Degrading to InputIterator implementation is quite a pessimization. What really should happen is the library should
Something like Also, I feel like of the two new tests the first one is unnecessary. |
|
Reworked as suggested: on reallocation the new characters are copied into the new storage while the old buffer is still live, then the old characters, then the storages are swapped and the old one destroyed. One deviation from the sketch: the new capacity comes from string_impl::growth(size + n, capacity()) rather than size() + n, so the growth factor stays consistent with string_impl::append and repeated appends remain amortised. Also dropped the first test. The string suite passes ASAN-clean, as does an aliasing stress (whole and partial self-append, with and without spare capacity, checked against std::string semantics). |
|
|


Repro: on a heap string with no spare capacity, self-append through the iterator overload, e.g.
s.append(s.begin(), s.end())ors.append(s.subview(k).begin(), s.subview(k).end()).Cause: append(InputIt, InputIt) sends random-access iterators to a path that grows the buffer with string_impl::append and then std::copy(first, last, out). The append reallocates and frees the old buffer that [first, last) points into, so the copy reads freed memory (ASAN heap-use-after-free at string.hpp:206). The string_view and input-iterator overloads already avoid this; the random-access one copied straight from the aliased source.
Fix: when the append would reallocate, buffer the characters through the input-iterator path first, and keep the in-place copy when there is spare capacity. std::string defines this overload as append(basic_string(first, last)), so self-referential ranges are well defined.
Regression tests in test/string.cpp self-append through the iterator overload with capacity consumed so the append reallocates; they report a use-after-free before the change and pass after.