https://gcc.gnu.org/g:4655e9c9eef67fde3e7e0b95a218a13ff5b33c1d

commit r13-9466-g4655e9c9eef67fde3e7e0b95a218a13ff5b33c1d
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Mon Dec 16 17:42:24 2024 +0000

    libstdc++: Fix std::deque::insert(pos, first, last) undefined behaviour 
[PR118035]
    
    Inserting an empty range into a std::deque results in undefined calls to
    either std::copy, std::copy_backward, std::move, or std::move_backward.
    We call those algos with invalid arguments where the output range is the
    same as the input range, e.g.  std::copy(first, last, first) which
    violates the preconditions for the algorithms.
    
    This fix simply returns early if there's nothing to insert. Most callers
    already ensure that we don't even call _M_range_insert_aux with an empty
    range, but some callers don't. Rather than checking for n == 0 in each
    of the callers, this just does the check once and uses __builtin_expect
    to treat empty insertions as unlikely.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/118035
            * include/bits/deque.tcc (_M_range_insert_aux): Return
            immediately if inserting an empty range.
            * testsuite/23_containers/deque/modifiers/insert/118035.cc: New
            test.
    
    (cherry picked from commit b273e25e11c842a5729d0e03c85088cf5ba8e06c)

Diff:
---
 libstdc++-v3/include/bits/deque.tcc                |  3 +++
 .../23_containers/deque/modifiers/insert/118035.cc | 26 ++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/libstdc++-v3/include/bits/deque.tcc 
b/libstdc++-v3/include/bits/deque.tcc
index a212b8a69403..45a6feebe991 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -601,6 +601,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                          std::forward_iterator_tag)
       {
        const size_type __n = std::distance(__first, __last);
+       if (__builtin_expect(__n == 0, 0))
+         return;
+
        if (__pos._M_cur == this->_M_impl._M_start._M_cur)
          {
            iterator __new_start = _M_reserve_elements_at_front(__n);
diff --git 
a/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/118035.cc 
b/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/118035.cc
new file mode 100644
index 000000000000..a37d3dc3d04c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/118035.cc
@@ -0,0 +1,26 @@
+// { dg-do run }
+
+#include <deque>
+#include <testsuite_hooks.h>
+
+struct Sparks
+{
+  Sparks& operator=(const Sparks& s)
+  {
+    VERIFY( this != &s ); // This town ain't big enough for the both of us.
+    return *this;
+  }
+};
+
+void
+test_pr118035()
+{
+  std::deque<Sparks> d(3, Sparks());
+  Sparks s[1];
+  d.insert(d.begin() + 1, s, s);
+}
+
+int main()
+{
+  test_pr118035();
+}

Reply via email to