[PATCH] Fix -Wstringop-overflow warning in 23_containers/vector/types/1.cc
François Dumont
frs.dumont@gmail.com
Thu May 30 05:11:36 GMT 2024
Looks like this new version works the same to fix the warning without
the issues reported here.
All 23_containers/vector tests run in C++98/14/20 so far.
Ok to commit once I've complete the testsuite (or some bot did it for me
!) ?
I'll look for a PR to associate, if you have one in mind do not hesitate
to tell me.
François
On 28/05/2024 12:28, Jonathan Wakely wrote:
> On 27/05/24 22:07 +0200, François Dumont wrote:
>> In C++98 this test fails with:
>>
>> Excess errors:
>> /home/fdumont/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_algobase.h:452:
>> warning: 'void* __builtin_memcpy(void*, const void*, long unsigned
>> int)' writing between 2 and 9223372036854775806 bytes into a region
>> of size 0 overflows the destination [-Wstringop-overflow=]
>>
>> The attached patch avoids this warning.
>>
>> libstdc++: Fix -Wstringop-overflow warning coming from std::vector
>>
>> Make vector<>::_M_range_insert implementation more transparent to
>> the compiler checks.
>>
>> Extend local copies of members to the whole method scope so that
>> all branches benefit
>> from those.
>>
>> libstdc++-v3/ChangeLog:
>>
>> * include/bits/vector.tcc
>> (std::vector<>::_M_range_insert(iterator, _FwdIt, _FwdIt,
>> forward_iterator_tag)):
>> Use local copies of members to call the different
>> algorithms.
>>
>> Ok to commit if all tests passes ?
>>
>> François
>
>> diff --git a/libstdc++-v3/include/bits/vector.tcc
>> b/libstdc++-v3/include/bits/vector.tcc
>> index 36b27dce7b9..671929dee55 100644
>> --- a/libstdc++-v3/include/bits/vector.tcc
>> +++ b/libstdc++-v3/include/bits/vector.tcc
>> @@ -885,83 +885,80 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> {
>> if (__first != __last)
>> {
>> + // Make local copies of these members because the compiler
>> + // thinks the allocator can alter them if 'this' is globally
>> + // reachable.
>> + pointer __start = this->_M_impl._M_start;
>> + pointer __end = this->_M_impl._M_end_of_storage;
>> + pointer __finish = this->_M_impl._M_finish;
>> + pointer __pos = __position.base();
>> + _Tp_alloc_type& __allocator = _M_get_Tp_allocator();
>> +
>> + if (__pos < __start || __finish < __pos)
>> + __builtin_unreachable();
>
> I don't think we should use __builtin_unreachable for something which
> is not an invariant of the class. The __position argument is supplied
> by the user, so we should not make promises about it being valid,
> because we can't know that.
>
> We can promise that __start <= __finish, and that __finish <= end,
> because we control those. We can't promise the user won't pass in a
> bad __position. Although it's undefined for the user to do that, using
> __builtin_unreachable() here makes the effects worse, and makes it
> harder to debug.
>
> Also, (__pos < __start) might already trigger undefined behaviour for
> fancy pointers, if they don't point to the same memory region.
>
> So this change is not OK.
>
>
>> +
>> const size_type __n = std::distance(__first, __last);
>> - if (size_type(this->_M_impl._M_end_of_storage
>> - - this->_M_impl._M_finish) >= __n)
>> + if (size_type(__end - __finish) >= __n)
>> {
>> - const size_type __elems_after = end() - __position;
>> - pointer __old_finish(this->_M_impl._M_finish);
>> + const size_type __elems_after = __end - __pos;
>> + pointer __old_finish(__finish);
>> if (__elems_after > __n)
>> {
>> _GLIBCXX_ASAN_ANNOTATE_GROW(__n);
>> - std::__uninitialized_move_a(this->_M_impl._M_finish - __n,
>> - this->_M_impl._M_finish,
>> - this->_M_impl._M_finish,
>> - _M_get_Tp_allocator());
>> - this->_M_impl._M_finish += __n;
>> + __finish = std::__uninitialized_move_a
>> + (__finish - __n, __finish, __finish, __allocator);
>> _GLIBCXX_ASAN_ANNOTATE_GREW(__n);
>> - _GLIBCXX_MOVE_BACKWARD3(__position.base(),
>> - __old_finish - __n, __old_finish);
>> - std::copy(__first, __last, __position);
>> + _GLIBCXX_MOVE_BACKWARD3
>> + (__pos, __old_finish - __n, __old_finish);
>> + std::copy(__first, __last, __pos);
>> }
>> else
>> {
>> _ForwardIterator __mid = __first;
>> std::advance(__mid, __elems_after);
>> _GLIBCXX_ASAN_ANNOTATE_GROW(__n);
>> - std::__uninitialized_copy_a(__mid, __last,
>> - this->_M_impl._M_finish,
>> - _M_get_Tp_allocator());
>> - this->_M_impl._M_finish += __n - __elems_after;
>> + __finish = std::__uninitialized_copy_a
>> + (__mid, __last, __finish, __allocator);
>> _GLIBCXX_ASAN_ANNOTATE_GREW(__n - __elems_after);
>> - std::__uninitialized_move_a(__position.base(),
>> - __old_finish,
>> - this->_M_impl._M_finish,
>> - _M_get_Tp_allocator());
>> - this->_M_impl._M_finish += __elems_after;
>> + __finish = std::__uninitialized_move_a
>> + (__pos, __old_finish, __finish, __allocator);
>> _GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after);
>> - std::copy(__first, __mid, __position);
>> + std::copy(__first, __mid, __pos);
>> }
>> +
>> + this->_M_impl._M_finish = __finish;
>> }
>> else
>> {
>> - // Make local copies of these members because the compiler
>> - // thinks the allocator can alter them if 'this' is globally
>> - // reachable.
>> - pointer __old_start = this->_M_impl._M_start;
>> - pointer __old_finish = this->_M_impl._M_finish;
>> -
>> + const size_type __size = size_type(__finish - __start);
>> const size_type __len =
>> _M_check_len(__n, "vector::_M_range_insert");
>> + if (__len < __n + __size)
>> + __builtin_unreachable();
>> +
>> pointer __new_start(this->_M_allocate(__len));
>> pointer __new_finish(__new_start);
>> __try
>> {
>> __new_finish
>> = std::__uninitialized_move_if_noexcept_a
>> - (__old_start, __position.base(),
>> - __new_start, _M_get_Tp_allocator());
>> + (__start, __pos, __new_start, __allocator);
>> __new_finish
>> - = std::__uninitialized_copy_a(__first, __last,
>> - __new_finish,
>> - _M_get_Tp_allocator());
>> + = std::__uninitialized_copy_a
>> + (__first, __last, __new_finish, __allocator);
>> __new_finish
>> = std::__uninitialized_move_if_noexcept_a
>> - (__position.base(), __old_finish,
>> - __new_finish, _M_get_Tp_allocator());
>> + (__pos, __finish, __new_finish, __allocator);
>> }
>> __catch(...)
>> {
>> - std::_Destroy(__new_start, __new_finish,
>> - _M_get_Tp_allocator());
>> + std::_Destroy(__new_start, __new_finish, __allocator);
>> _M_deallocate(__new_start, __len);
>> __throw_exception_again;
>> }
>> - std::_Destroy(__old_start, __old_finish,
>> - _M_get_Tp_allocator());
>> + std::_Destroy(__start, __finish, __allocator);
>> _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(__old_start,
>> - this->_M_impl._M_end_of_storage - __old_start);
>> + _M_deallocate(__start, __end - __start);
>> this->_M_impl._M_start = __new_start;
>> this->_M_impl._M_finish = __new_finish;
>> this->_M_impl._M_end_of_storage = __new_start + __len;
>
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index 36b27dce7b9..5b3c5c2ecd5 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -885,83 +885,78 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
if (__first != __last)
{
+ // Make local copies of these members because the compiler
+ // thinks the allocator can alter them if 'this' is globally
+ // reachable.
+ pointer __start = this->_M_impl._M_start;
+ pointer __end = this->_M_impl._M_end_of_storage;
+ pointer __finish = this->_M_impl._M_finish;
+ pointer __pos = __position.base();
+ _Tp_alloc_type& __allocator = _M_get_Tp_allocator();
+
const size_type __n = std::distance(__first, __last);
- if (size_type(this->_M_impl._M_end_of_storage
- - this->_M_impl._M_finish) >= __n)
+ if (size_type(__end - __finish) >= __n)
{
- const size_type __elems_after = end() - __position;
- pointer __old_finish(this->_M_impl._M_finish);
+ const size_type __elems_after = __end - __pos;
+ pointer __old_finish(__finish);
if (__elems_after > __n)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
- std::__uninitialized_move_a(this->_M_impl._M_finish - __n,
- this->_M_impl._M_finish,
- this->_M_impl._M_finish,
- _M_get_Tp_allocator());
- this->_M_impl._M_finish += __n;
+ this->_M_impl._M_finish = std::__uninitialized_move_a
+ (__finish - __n, __finish, __finish, __allocator);
_GLIBCXX_ASAN_ANNOTATE_GREW(__n);
- _GLIBCXX_MOVE_BACKWARD3(__position.base(),
- __old_finish - __n, __old_finish);
- std::copy(__first, __last, __position);
+ _GLIBCXX_MOVE_BACKWARD3
+ (__pos, __old_finish - __n, __old_finish);
+ std::copy(__first, __last, __pos);
}
else
{
_ForwardIterator __mid = __first;
std::advance(__mid, __elems_after);
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
- std::__uninitialized_copy_a(__mid, __last,
- this->_M_impl._M_finish,
- _M_get_Tp_allocator());
- this->_M_impl._M_finish += __n - __elems_after;
+ this->_M_impl._M_finish = __finish =
+ std::__uninitialized_copy_a
+ (__mid, __last, __finish, __allocator);
_GLIBCXX_ASAN_ANNOTATE_GREW(__n - __elems_after);
- std::__uninitialized_move_a(__position.base(),
- __old_finish,
- this->_M_impl._M_finish,
- _M_get_Tp_allocator());
- this->_M_impl._M_finish += __elems_after;
+ this->_M_impl._M_finish = std::__uninitialized_move_a
+ (__pos, __old_finish, __finish, __allocator);
_GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after);
- std::copy(__first, __mid, __position);
+ std::copy(__first, __mid, __pos);
}
}
else
{
- // Make local copies of these members because the compiler
- // thinks the allocator can alter them if 'this' is globally
- // reachable.
- pointer __old_start = this->_M_impl._M_start;
- pointer __old_finish = this->_M_impl._M_finish;
-
+ const size_type __size = size_type(__finish - __start);
const size_type __len =
_M_check_len(__n, "vector::_M_range_insert");
+ if (__len < __n + __size)
+ __builtin_unreachable();
+
pointer __new_start(this->_M_allocate(__len));
pointer __new_finish(__new_start);
+ _Guard_alloc __guard(__new_start, __len, *this);
__try
{
__new_finish
= std::__uninitialized_move_if_noexcept_a
- (__old_start, __position.base(),
- __new_start, _M_get_Tp_allocator());
+ (__start, __pos, __new_start, __allocator);
__new_finish
- = std::__uninitialized_copy_a(__first, __last,
- __new_finish,
- _M_get_Tp_allocator());
+ = std::__uninitialized_copy_a
+ (__first, __last, __new_finish, __allocator);
__new_finish
= std::__uninitialized_move_if_noexcept_a
- (__position.base(), __old_finish,
- __new_finish, _M_get_Tp_allocator());
+ (__pos, __finish, __new_finish, __allocator);
+
+ __guard._M_storage = __start;
+ __guard._M_len = size_type(__end - __start);
}
__catch(...)
- {
- std::_Destroy(__new_start, __new_finish,
- _M_get_Tp_allocator());
- _M_deallocate(__new_start, __len);
- __throw_exception_again;
- }
- std::_Destroy(__old_start, __old_finish,
- _M_get_Tp_allocator());
+ {
+ std::_Destroy(__new_start, __new_finish, __allocator);
+ __throw_exception_again;
+ }
+ std::_Destroy(__start, __finish, __allocator);
_GLIBCXX_ASAN_ANNOTATE_REINIT;
- _M_deallocate(__old_start,
- this->_M_impl._M_end_of_storage - __old_start);
this->_M_impl._M_start = __new_start;
this->_M_impl._M_finish = __new_finish;
this->_M_impl._M_end_of_storage = __new_start + __len;
More information about the Libstdc++
mailing list