Truncate absl::InlinedVector prior to moving, in case moves throw an exception PiperOrigin-RevId: 947105228 Change-Id: I697670634bf8c9c88f10a85cf18e4c9ce2deb268
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 744d3d9..2694a5d 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h
@@ -948,6 +948,12 @@ storage_.GetAllocator(), data(), size()); storage_.DeallocateIfAllocated(); + if constexpr (!std::is_nothrow_move_constructible_v<value_type>) { + // Reset the size to zero before moving to avoid leaking freed memory if + // an exception is thrown. + storage_.SetInlinedSize(0); + } + IteratorValueAdapter<A, MoveIterator<A>> other_values( MoveIterator<A>(other.storage_.GetInlinedData())); inlined_vector_internal::ConstructElements<A>(
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 6ed9886..c9af35d 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc
@@ -2360,6 +2360,27 @@ EXPECT_EQ(alive_count, 0) << "Mismatch between constructor and destructor calls!"; } + +// Ensures that an exception thrown during move construction doesn't leave the +// inlined vector in a bad state. Needs ASAN for reliable detection. +TEST(InlinedVectorTest, TruncatesBeforeThrowingMoveConstruction) { + using Vec = absl::InlinedVector<ThrowOnMove, 1>; + int count = 0; + { + Vec dest; + dest.reserve(dest.capacity() + 1); // force heap allocation + dest.emplace_back(&count); + + Vec src; + src.emplace_back(&count).should_throw = true; + + try { + dest = std::move(src); + } catch (const std::runtime_error&) { + } + } + EXPECT_EQ(count, 0); +} #endif } // anonymous namespace