utils/chunked_vector: Fix sigsegv during reserve()

Fixes the case of make_room() invoked with last_chunk_capacity_deficit
but _size not in the last reserved chunk.

Found during code review, no known user impact.

Fixes #10363.

Message-Id: <20220411222605.641614-1-tgrabiec@scylladb.com>
This commit is contained in:
Tomasz Grabiec
2022-04-12 00:26:05 +02:00
committed by Avi Kivity
parent 546ee814dd
commit 01eeb33c6e
2 changed files with 32 additions and 1 deletions

View File

@@ -178,3 +178,32 @@ BOOST_AUTO_TEST_CASE(tests_reserve_partial) {
BOOST_REQUIRE_EQUAL(v.capacity(), orig_size);
}
}
// Tests the case of make_room() invoked with last_chunk_capacity_deficit but _size not in
// the last reserved chunk.
BOOST_AUTO_TEST_CASE(test_shrinking_and_expansion_involving_chunk_boundary) {
using vector_type = utils::chunked_vector<std::unique_ptr<uint64_t>>;
vector_type v;
// Fill two chunks
v.reserve(vector_type::max_chunk_capacity() * 3 / 2);
for (uint64_t i = 0; i < vector_type::max_chunk_capacity() * 3 / 2; ++i) {
v.emplace_back(std::make_unique<uint64_t>(i));
}
// Make the last chunk smaller than max size to trigger the last_chunk_capacity_deficit path in make_room()
v.shrink_to_fit();
// Leave the last chunk reserved but empty
for (uint64_t i = 0; i < vector_type::max_chunk_capacity(); ++i) {
v.pop_back();
}
// Try to reserve more than the currently reserved capacity and trigger last_chunk_capacity_deficit path
// with _size not in the last chunk. Should not sigsegv.
v.reserve(vector_type::max_chunk_capacity() * 4);
for (uint64_t i = 0; i < vector_type::max_chunk_capacity() * 2; ++i) {
v.emplace_back(std::make_unique<uint64_t>(i));
}
}

View File

@@ -376,7 +376,9 @@ chunked_vector<T, max_contiguous_allocation>::make_room(size_t n, bool stop_afte
auto new_last_chunk_capacity = last_chunk_capacity + capacity_increase;
// FIXME: realloc? maybe not worth the complication; only works for PODs
auto new_last_chunk = new_chunk(new_last_chunk_capacity);
migrate(addr(_capacity - last_chunk_capacity), addr(_size), new_last_chunk.get());
if (_size > _capacity - last_chunk_capacity) {
migrate(addr(_capacity - last_chunk_capacity), addr(_size), new_last_chunk.get());
}
_chunks.back() = std::move(new_last_chunk);
_capacity += capacity_increase;
}