From 47f190bc47bb836987dd1d3273cd2950097d9718 Mon Sep 17 00:00:00 2001 From: Martin Mirchev Date: Fri, 30 Oct 2026 13:35:00 +0800 Subject: [PATCH] Fix MakeSharedCappedKeySegmentsExtractor with more than 6 widths For more than 6 byte widths, MakeSharedCappedKeySegmentsExtractor() returns a DynamicCappedKeySegmentsExtractor, whose constructor assigned ideal_ends_[i] without sizing the vector first. Creating such an extractor wrote past the end of an empty vector (a segfault in DBBloomFilterTest), and Extract() would have returned no segments. Size ideal_ends_ before filling it, and cover a 7-width extractor in DBBloomFilterTest.FixedWidthSegments. --- db/db_bloom_filter_test.cc | 14 ++++++++++++++ db/experimental.cc | 1 + .../capped_key_segments_extractor_many_widths.md | 1 + 3 files changed, 16 insertions(+) create mode 100644 unreleased_history/bug_fixes/capped_key_segments_extractor_many_widths.md diff --git a/db/db_bloom_filter_test.cc b/db/db_bloom_filter_test.cc index 3d52a873715..915c3324379 100644 --- a/db/db_bloom_filter_test.cc +++ b/db/db_bloom_filter_test.cc @@ -4288,6 +4288,9 @@ TEST_F(DBBloomFilterTest, FixedWidthSegments) { auto extractor4b0b = MakeSharedCappedKeySegmentsExtractor({4, 0}); auto extractor4b0b4b = MakeSharedCappedKeySegmentsExtractor({4, 0, 4}); auto extractor1b3b0b4b = MakeSharedCappedKeySegmentsExtractor({1, 3, 0, 4}); + // More than 6 segments uses a different implementation + auto extractor1b2b0b1b1b1b1b = + MakeSharedCappedKeySegmentsExtractor({1, 2, 0, 1, 1, 1, 1}); ASSERT_EQ(extractor_none->GetId(), "CappedKeySegmentsExtractor"); ASSERT_EQ(extractor0b->GetId(), "CappedKeySegmentsExtractor0b"); @@ -4296,6 +4299,8 @@ TEST_F(DBBloomFilterTest, FixedWidthSegments) { ASSERT_EQ(extractor4b0b->GetId(), "CappedKeySegmentsExtractor4b0b"); ASSERT_EQ(extractor4b0b4b->GetId(), "CappedKeySegmentsExtractor4b0b4b"); ASSERT_EQ(extractor1b3b0b4b->GetId(), "CappedKeySegmentsExtractor1b3b0b4b"); + ASSERT_EQ(extractor1b2b0b1b1b1b1b->GetId(), + "CappedKeySegmentsExtractor1b2b0b1b1b1b1b"); using V = std::vector; ASSERT_EQ(V({}), ExtractedSizes(*extractor_none, {})); @@ -4332,6 +4337,15 @@ TEST_F(DBBloomFilterTest, FixedWidthSegments) { ASSERT_EQ(V({4, 0, 4}), ExtractedSizes(*extractor4b0b4b, "blah12345")); ASSERT_EQ(V({1, 3, 0, 4}), ExtractedSizes(*extractor1b3b0b4b, "blah12345")); + ASSERT_EQ(V({0, 0, 0, 0, 0, 0, 0}), + ExtractedSizes(*extractor1b2b0b1b1b1b1b, {})); + ASSERT_EQ(V({1, 2, 0, 1, 0, 0, 0}), + ExtractedSizes(*extractor1b2b0b1b1b1b1b, "blah")); + ASSERT_EQ(V({1, 2, 0, 1, 1, 1, 1}), + ExtractedSizes(*extractor1b2b0b1b1b1b1b, "blah123")); + ASSERT_EQ(V({1, 2, 0, 1, 1, 1, 1}), + ExtractedSizes(*extractor1b2b0b1b1b1b1b, "blah12345")); + // Filter config for second and fourth segment auto filter1 = MakeSharedReverseBytewiseMinMaxSQFC(experimental::SelectKeySegment(1)); diff --git a/db/experimental.cc b/db/experimental.cc index 71cef247b55..6f64ecabef9 100644 --- a/db/experimental.cc +++ b/db/experimental.cc @@ -255,6 +255,7 @@ class DynamicCappedKeySegmentsExtractor : public KeySegmentsExtractor { public: DynamicCappedKeySegmentsExtractor(const std::vector& byte_widths) { id_ = kName(); + ideal_ends_.resize(byte_widths.size()); uint32_t prev_end = 0; for (size_t i = 0; i < byte_widths.size(); ++i) { prev_end = prev_end + byte_widths[i]; diff --git a/unreleased_history/bug_fixes/capped_key_segments_extractor_many_widths.md b/unreleased_history/bug_fixes/capped_key_segments_extractor_many_widths.md new file mode 100644 index 00000000000..a9cf0648109 --- /dev/null +++ b/unreleased_history/bug_fixes/capped_key_segments_extractor_many_widths.md @@ -0,0 +1 @@ +Fixed a crash in experimental `MakeSharedCappedKeySegmentsExtractor()` with more than 6 byte widths: the extractor wrote the segment ends into an empty vector.