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;
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;

Reply via email to