From 3bdb9015b1226c33231794c1ace84cd5b8a7e6f3 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 30 Apr 2014 15:49:06 +0000 Subject: [PATCH] Fix a use of uninitialized memory in SmallVector's move-assignment operator. When we were moving from a larger vector to a smaller one but didn't need to re-allocate, we would move-assign over uninitialized memory in the target, then move-construct that same data again. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@207663 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/SmallVector.h | 2 +- unittests/ADT/SmallVectorTest.cpp | 63 ++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/include/llvm/ADT/SmallVector.h b/include/llvm/ADT/SmallVector.h index df46f919118..dcf0354860f 100644 --- a/include/llvm/ADT/SmallVector.h +++ b/include/llvm/ADT/SmallVector.h @@ -805,7 +805,7 @@ SmallVectorImpl &SmallVectorImpl::operator=(SmallVectorImpl &&RHS) { this->grow(RHSSize); } else if (CurSize) { // Otherwise, use assignment for the already-constructed elements. - this->move(RHS.begin(), RHS.end(), this->begin()); + this->move(RHS.begin(), RHS.begin()+CurSize, this->begin()); } // Move-construct the new elements in place. diff --git a/unittests/ADT/SmallVectorTest.cpp b/unittests/ADT/SmallVectorTest.cpp index 90c7982699a..58f55914ba5 100644 --- a/unittests/ADT/SmallVectorTest.cpp +++ b/unittests/ADT/SmallVectorTest.cpp @@ -29,27 +29,43 @@ private: static int numDestructorCalls; static int numAssignmentCalls; + bool constructed; int value; public: - Constructable() : value(0) { + Constructable() : constructed(true), value(0) { ++numConstructorCalls; } - Constructable(int val) : value(val) { + Constructable(int val) : constructed(true), value(val) { ++numConstructorCalls; } - Constructable(const Constructable & src) { + Constructable(const Constructable & src) : constructed(true) { + value = src.value; + ++numConstructorCalls; + } + + Constructable(Constructable && src) : constructed(true) { value = src.value; ++numConstructorCalls; } ~Constructable() { + EXPECT_TRUE(constructed); ++numDestructorCalls; + constructed = false; } Constructable & operator=(const Constructable & src) { + EXPECT_TRUE(constructed); + value = src.value; + ++numAssignmentCalls; + return *this; + } + + Constructable & operator=(Constructable && src) { + EXPECT_TRUE(constructed); value = src.value; ++numAssignmentCalls; return *this; @@ -338,6 +354,36 @@ TYPED_TEST(SmallVectorTest, AssignTest) { this->assertValuesInOrder(this->theVector, 2u, 77, 77); } +// Move-assign test +TYPED_TEST(SmallVectorTest, MoveAssignTest) { + SCOPED_TRACE("MoveAssignTest"); + + // Set up our vector with a single element, but enough capacity for 4. + this->theVector.reserve(4); + this->theVector.push_back(Constructable(1)); + + // Set up the other vector with 2 elements. + this->otherVector.push_back(Constructable(2)); + this->otherVector.push_back(Constructable(3)); + + // Move-assign from the other vector. + this->theVector = std::move(this->otherVector); + + // Make sure we have the right result. + this->assertValuesInOrder(this->theVector, 2u, 2, 3); + + // Make sure the # of constructor/destructor calls line up. There + // are two live objects after clearing the other vector. + this->otherVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls()-2, + Constructable::getNumDestructorCalls()); + + // There shouldn't be any live objects any more. + this->theVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls(), + Constructable::getNumDestructorCalls()); +} + // Erase a single element TYPED_TEST(SmallVectorTest, EraseTest) { SCOPED_TRACE("EraseTest"); @@ -455,13 +501,12 @@ TYPED_TEST(SmallVectorTest, DirectVectorTest) { this->theVector.reserve(4); EXPECT_LE(4u, this->theVector.capacity()); EXPECT_EQ(0, Constructable::getNumConstructorCalls()); - this->theVector.end()[0] = 1; - this->theVector.end()[1] = 2; - this->theVector.end()[2] = 3; - this->theVector.end()[3] = 4; - this->theVector.set_size(4); + this->theVector.push_back(1); + this->theVector.push_back(2); + this->theVector.push_back(3); + this->theVector.push_back(4); EXPECT_EQ(4u, this->theVector.size()); - EXPECT_EQ(4, Constructable::getNumConstructorCalls()); + EXPECT_EQ(8, Constructable::getNumConstructorCalls()); EXPECT_EQ(1, this->theVector[0].getValue()); EXPECT_EQ(2, this->theVector[1].getValue()); EXPECT_EQ(3, this->theVector[2].getValue());