From 41fe01ecffa994a96f69435888ebfd111fd11da0 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 7 Apr 2022 19:40:23 +0200 Subject: [PATCH] utils/chunked_managed_vector: Fix corruption in case there is more than one chunk If reserve() allocates more than one chunk, push_back() should not work with the last chunk. This can result in items being pushed to the wrong chunk, breaking internal invariants. Also, pop_back() should not work with the last chunk. This breaks when there is more than one chunk. Currently, the container is only used in the sstable partition index cache. Manifests by crashes in sstable reader which touch sstables which have partition index pages with more than 1638 partition entries. Introduced in 78e5b9fd8542a1621f6e49cd368725e089813629 (4.6.0) Fixes #10290 Message-Id: <20220407174023.527059-1-tgrabiec@scylladb.com> --- test/boost/chunked_managed_vector_test.cc | 63 +++++++++++++++++++++++ utils/lsa/chunked_managed_vector.hh | 11 ++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/test/boost/chunked_managed_vector_test.cc b/test/boost/chunked_managed_vector_test.cc index 22415c1632..f774b67faa 100644 --- a/test/boost/chunked_managed_vector_test.cc +++ b/test/boost/chunked_managed_vector_test.cc @@ -12,6 +12,8 @@ #include #include #include "utils/lsa/chunked_managed_vector.hh" +#include "utils/managed_ref.hh" +#include "test/lib/log.hh" #include #include @@ -203,3 +205,64 @@ SEASTAR_TEST_CASE(tests_reserve_partial) { }); return make_ready_future<>(); } + +SEASTAR_TEST_CASE(test_clear_and_release) { + region region; + allocating_section as; + + with_allocator(region.allocator(), [&] { + lsa::chunked_managed_vector> v; + + for (uint64_t i = 1; i < 4000; ++i) { + as(region, [&] { + v.emplace_back(make_managed(i)); + }); + } + + v.clear_and_release(); + }); + + return make_ready_future<>(); +} + +SEASTAR_TEST_CASE(test_chunk_reserve) { + region region; + allocating_section as; + + for (auto conf : + { // std::make_pair(reserve size, push count) + std::make_pair(0, 4000), + std::make_pair(100, 4000), + std::make_pair(200, 4000), + std::make_pair(1000, 4000), + std::make_pair(2000, 4000), + std::make_pair(3000, 4000), + std::make_pair(5000, 4000), + std::make_pair(500, 8000), + std::make_pair(1000, 8000), + std::make_pair(2000, 8000), + std::make_pair(8000, 500), + }) + { + with_allocator(region.allocator(), [&] { + auto [reserve_size, push_count] = conf; + testlog.info("Testing reserve({}), {}x emplace_back()", reserve_size, push_count); + lsa::chunked_managed_vector> v; + v.reserve(reserve_size); + uint64_t seed = rand(); + for (uint64_t i = 0; i < push_count; ++i) { + as(region, [&] { + v.emplace_back(make_managed(seed + i)); + BOOST_REQUIRE(**v.begin() == seed); + }); + } + auto v_it = v.begin(); + for (uint64_t i = 0; i < push_count; ++i) { + BOOST_REQUIRE(**v_it++ == seed + i); + } + v.clear_and_release(); + }); + } + + return make_ready_future<>(); +} diff --git a/utils/lsa/chunked_managed_vector.hh b/utils/lsa/chunked_managed_vector.hh index e7a6291659..cbf83eef79 100644 --- a/utils/lsa/chunked_managed_vector.hh +++ b/utils/lsa/chunked_managed_vector.hh @@ -60,6 +60,9 @@ private: throw std::out_of_range("chunked_managed_vector out of range access"); } } + chunk_ptr& back_chunk() { + return _chunks[_size / max_chunk_capacity()]; + } static void migrate(T* begin, T* end, managed_vector& result); public: using value_type = T; @@ -106,24 +109,24 @@ public: void push_back(const T& x) { reserve_for_push_back(); - _chunks.back().emplace_back(x); + back_chunk().emplace_back(x); ++_size; } void push_back(T&& x) { reserve_for_push_back(); - _chunks.back().emplace_back(std::move(x)); + back_chunk().emplace_back(std::move(x)); ++_size; } template T& emplace_back(Args&&... args) { reserve_for_push_back(); - auto& ret = _chunks.back().emplace_back(std::forward(args)...); + auto& ret = back_chunk().emplace_back(std::forward(args)...); ++_size; return ret; } void pop_back() { --_size; - _chunks.back().pop_back(); + back_chunk().pop_back(); } const T& back() const { return *addr(_size - 1);