From 67391f0a572ddc9c53bdff35dac893b07a862fe5 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 22 Nov 2017 01:05:52 +0100 Subject: Fix aliasing problem in QVector::removeAll() Since removeAll() takes its argument by cref, if passing a reference to an element of the container to removeAll(), the element may be deleted (overwritten) by anyother value, leading to UB. Add a test that actually happens to fail for me without the patch, even though that might not be guaranteed (we may invoke UB). Change-Id: If8c795113aeb515f4a9bdf1e072395b932295667 Reviewed-by: Thiago Macieira --- src/corelib/tools/qvector.h | 5 +++-- tests/auto/corelib/tools/qvector/tst_qvector.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index da15d401e3..74c37faad0 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -164,9 +164,10 @@ public: const const_iterator ce = this->cend(), cit = std::find(this->cbegin(), ce, t); if (cit == ce) return 0; - // next operation detaches, so ce, cit may become invalidated: + // next operation detaches, so ce, cit, t may become invalidated: + const T tCopy = t; const int firstFoundIdx = std::distance(this->cbegin(), cit); - const iterator e = end(), it = std::remove(begin() + firstFoundIdx, e, t); + const iterator e = end(), it = std::remove(begin() + firstFoundIdx, e, tCopy); const int result = std::distance(it, e); erase(it, e); return result; diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index 374fec221e..6975452d76 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -245,6 +245,7 @@ private slots: void qhashInt() const { qhash(); } void qhashMovable() const { qhash(); } void qhashCustom() const { qhash(); } + void removeAllWithAlias() const; void removeInt() const; void removeMovable() const; void removeCustom() const; @@ -1722,6 +1723,13 @@ void tst_QVector::prependCustom() const QCOMPARE(instancesCount, Custom::counter.loadAcquire()); } +void tst_QVector::removeAllWithAlias() const +{ + QVector strings; + strings << "One" << "Two" << "Three" << "One" /* must be distinct, but equal */; + QCOMPARE(strings.removeAll(strings.front()), 2); // will trigger asan/ubsan +} + template void tst_QVector::remove() const { -- cgit v1.2.1