aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-12-16 17:42:24 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2024-12-17 18:54:17 +0000
commitb273e25e11c842a5729d0e03c85088cf5ba8e06c (patch)
tree3c8136973abb80e378877ce78dacd6b781a3da70
parentef458b3fa75537cb2d16f4ce61bc52642ddefd8a (diff)
downloadgcc-b273e25e11c842a5729d0e03c85088cf5ba8e06c.zip
gcc-b273e25e11c842a5729d0e03c85088cf5ba8e06c.tar.gz
gcc-b273e25e11c842a5729d0e03c85088cf5ba8e06c.tar.bz2
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.
-rw-r--r--libstdc++-v3/include/bits/deque.tcc3
-rw-r--r--libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/118035.cc26
2 files changed, 29 insertions, 0 deletions
diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index ee03c91..05929e9 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 0000000..a37d3dc
--- /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();
+}