Prevent double destroy in InlinedVector swap on exception unwinding PiperOrigin-RevId: 946273899 Change-Id: I8d7e8881f47e09ba6201daba6c04f512cccf3df9
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 67b4368..6ed9886 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc
@@ -2313,4 +2313,53 @@ EXPECT_THAT(v, IsEmpty()); } +#ifdef ABSL_HAVE_EXCEPTIONS +struct ThrowOnMove { + static constexpr uint32_t kAlive = 0xA11FE123; + static constexpr uint32_t kDestroyed = 0xDEADBEEF; + + explicit ThrowOnMove(int* count) : alive_count(count), sentinel(kAlive) { + if (alive_count) ++(*alive_count); + } + ThrowOnMove(const ThrowOnMove&) = delete; + ThrowOnMove& operator=(const ThrowOnMove&) = delete; + ThrowOnMove(ThrowOnMove&& other) + : alive_count(other.alive_count), sentinel(kAlive) { + if (other.should_throw) throw std::runtime_error("ThrowOnMove"); + if (alive_count) ++(*alive_count); + } + ~ThrowOnMove() { + EXPECT_EQ(sentinel, kAlive) + << "Double destroy detected: destructor called twice on memory slot!"; + sentinel = kDestroyed; + if (alive_count) { + EXPECT_GT(*alive_count, 0) + << "More destructors called than constructors!"; + --(*alive_count); + } + } + + int* alive_count = nullptr; + uint32_t sentinel = kDestroyed; + bool should_throw = false; +}; + +TEST(InlinedVectorTest, SwapExceptionSafety) { + int alive_count = 0; + try { + absl::InlinedVector<ThrowOnMove, 2> a; + a.emplace_back(&alive_count); + absl::InlinedVector<ThrowOnMove, 2> b; + b.emplace_back(&alive_count); + b[0].should_throw = true; + a.swap(b); + FAIL() << "Expected swap to throw std::runtime_error"; + } catch (const std::runtime_error&) { + // Expected exception from throwing move-constructor inside swap. + } + EXPECT_EQ(alive_count, 0) + << "Mismatch between constructor and destructor calls!"; +} +#endif + } // anonymous namespace
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 4e9b87e..e6491ba 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h
@@ -1038,10 +1038,22 @@ ValueType<A> tmp(std::move(*a)); AllocatorTraits<A>::destroy(allocator_a, a); - AllocatorTraits<A>::construct(allocator_b, a, std::move(*b)); + ABSL_INTERNAL_TRY { + AllocatorTraits<A>::construct(allocator_b, a, std::move(*b)); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits<A>::construct(allocator_a, a, std::move(tmp)); + ABSL_INTERNAL_RETHROW; + } AllocatorTraits<A>::destroy(allocator_b, b); - AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp)); + ABSL_INTERNAL_TRY { + AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp)); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits<A>::construct(allocator_b, b, std::move(*a)); + ABSL_INTERNAL_RETHROW; + } } }