Skip to content

Commit 30e8e8c

Browse files
authored
datastore: fix out-of-bounds index handling in snapshots BTree (#2883)
1 parent f81ca64 commit 30e8e8c

File tree

1 file changed

+35
-20
lines changed
  • silkworm/db/datastore/snapshots/btree

1 file changed

+35
-20
lines changed

silkworm/db/datastore/snapshots/btree/btree.cpp

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,16 @@ BTree::BTree(
2424

2525
using CompareResult = std::pair<int, BytesOrByteView>;
2626

27-
static CompareResult compare_key(
27+
static std::optional<CompareResult> compare_key(
2828
ByteView key,
2929
BTree::DataIndex key_index,
3030
const BTree::KeyValueIndex& index) {
3131
auto data_key = index.lookup_key(key_index);
32-
ensure(data_key.has_value(), [&] { return "out-of-bounds key=" + to_hex(key) + " data_index=" + std::to_string(key_index); });
32+
if (!data_key) {
33+
return std::nullopt;
34+
}
3335
int cmp = ByteView{*data_key}.compare(key);
34-
return {cmp, std::move(*data_key)};
35-
}
36-
37-
static BTree::KeyValueIndex::LookupResult lookup_key_value(
38-
ByteView key,
39-
BTree::DataIndex key_index,
40-
const BTree::KeyValueIndex& index) {
41-
auto result = index.lookup_key_value(key_index, key);
42-
ensure(result.has_value(), [&] { return "out-of-bounds key=" + to_hex(key) + " data_index=" + std::to_string(key_index); });
43-
return std::move(*result);
36+
return CompareResult{cmp, std::move(*data_key)};
4437
}
4538

4639
BTree::SeekResult BTree::seek(ByteView seek_key, const KeyValueIndex& index) {
@@ -55,8 +48,12 @@ BTree::SeekResult BTree::seek(ByteView seek_key, const KeyValueIndex& index) {
5548
auto [_, left_index, right_index] = binary_search_in_cache(seek_key); // left_index == right_index when key is found
5649
uint64_t median = 0;
5750
while (left_index < right_index) {
58-
if (right_index - left_index <= kDefaultBtreeStartSkip) { // found small range, faster to scan now
59-
const auto [cmp, key] = compare_key(seek_key, left_index, index);
51+
if (right_index - left_index <= kDefaultBtreeStartSkip) { // found a small range, faster to scan now
52+
const auto cmp_result = compare_key(seek_key, left_index, index);
53+
if (!cmp_result) {
54+
return {/*found=*/false, {}, {}, 0};
55+
}
56+
const auto [cmp, key] = *cmp_result;
6057
if (cmp == 0) {
6158
right_index = left_index;
6259
break;
@@ -71,7 +68,11 @@ BTree::SeekResult BTree::seek(ByteView seek_key, const KeyValueIndex& index) {
7168
break;
7269
}
7370
median = (left_index + right_index) >> 1;
74-
const auto [cmp, key] = compare_key(seek_key, median, index);
71+
const auto cmp_result = compare_key(seek_key, median, index);
72+
if (!cmp_result) {
73+
return {/*found=*/false, {}, {}, 0};
74+
}
75+
const auto [cmp, key] = *cmp_result;
7576
if (cmp == 0) {
7677
left_index = right_index = median;
7778
break;
@@ -107,7 +108,7 @@ std::optional<BytesOrByteView> BTree::get(ByteView key, const KeyValueIndex& ind
107108
}
108109
auto [_, left_index, right_index] = binary_search_in_cache(key); // left_index == right_index when key is found
109110
while (left_index < right_index) {
110-
if (right_index - left_index <= kDefaultBtreeStartSkip) { // found small range, faster to scan now
111+
if (right_index - left_index <= kDefaultBtreeStartSkip) { // found a small range, faster to scan now
111112
auto value = index.advance_key_value(left_index, key, right_index - left_index);
112113
if (!value) {
113114
left_index = right_index;
@@ -116,7 +117,11 @@ std::optional<BytesOrByteView> BTree::get(ByteView key, const KeyValueIndex& ind
116117
return value;
117118
}
118119
const uint64_t median = (left_index + right_index) >> 1;
119-
auto [cmp, optional_v] = lookup_key_value(key, median, index);
120+
const auto lookup_result = index.lookup_key_value(median, key);
121+
if (!lookup_result) {
122+
return std::nullopt;
123+
}
124+
const auto [cmp, optional_v] = *lookup_result;
120125
if (cmp == 0) {
121126
SILKWORM_ASSERT(optional_v);
122127
return optional_v;
@@ -127,7 +132,11 @@ std::optional<BytesOrByteView> BTree::get(ByteView key, const KeyValueIndex& ind
127132
left_index = median + 1;
128133
}
129134
}
130-
auto [cmp, optional_v] = lookup_key_value(key, left_index, index);
135+
const auto lookup_result = index.lookup_key_value(left_index, key);
136+
if (!lookup_result) {
137+
return std::nullopt;
138+
}
139+
const auto [cmp, optional_v] = *lookup_result;
131140
if (cmp != 0) {
132141
return std::nullopt;
133142
}
@@ -157,7 +166,9 @@ void BTree::warmup(const KeyValueIndex& index) {
157166
const size_t step = num_nodes_ < fanout_ ? 1 : fanout_; // cache all keys if less than M
158167
for (size_t i{step}; i < num_nodes_; i += step) {
159168
const size_t data_index = i - 1;
160-
auto [_, key] = compare_key({}, data_index, index);
169+
auto cmp_result = compare_key({}, data_index, index);
170+
if (!cmp_result) continue;
171+
auto [_, key] = *cmp_result;
161172
cache_.emplace_back(Node{data_index, Bytes{key}});
162173
cached_bytes += sizeof(Node) + ByteView{key}.size();
163174
}
@@ -185,7 +196,11 @@ BTree::Nodes BTree::decode_nodes(std::span<uint8_t> encoded_nodes) {
185196

186197
void BTree::check_against_data_keys(const KeyValueIndex& index) {
187198
for (const auto& node : cache_) {
188-
const auto [cmp, key] = compare_key(node.key, node.key_index, index);
199+
const auto cmp_result = compare_key(node.key, node.key_index, index);
200+
ensure(cmp_result.has_value(), [&] {
201+
return "out-of-bounds key=" + to_hex(node.key) + " data_index=" + std::to_string(node.key_index);
202+
});
203+
const auto [cmp, key] = *cmp_result;
189204
ensure(cmp == 0, [&]() {
190205
return "key mismatch node.key=" + to_hex(node.key) +
191206
" key=" + to_hex(key) +

0 commit comments

Comments
 (0)