Skip to content

Commit 0fb8766

Browse files
Eligioojsdanielh
authored andcommitted
Trie: reject ROOT-keyed items in put_chunk
1 parent 64d89e1 commit 0fb8766

1 file changed

Lines changed: 45 additions & 1 deletion

File tree

primitives/trie/src/trie.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
355355
value: Vec<u8>,
356356
missing_range: &Option<ops::RangeFrom<KeyNibbles>>,
357357
) {
358+
debug_assert!(
359+
!key.is_empty(),
360+
"empty (ROOT) key must be rejected at the put_chunk boundary",
361+
);
362+
358363
// Start by getting the root node.
359364
let mut cur_node = self
360365
.get_root(txn)
@@ -413,7 +418,8 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
413418
// with the given key. Update the value.
414419
if cur_node.key == *key {
415420
// Update the node and store it.
416-
// TODO This unwrap() will fail when attempting to store a value at the root node
421+
// `put_raw` rejects empty keys at its entry, so `cur_node` here never has
422+
// `root_data.is_some()`; `put_value` cannot return `RootCantHaveValue`.
417423
let prev_kind = cur_node.kind();
418424
let old_value = cur_node.put_value(value).unwrap();
419425
self.put_node(txn, &cur_node, old_value.into());
@@ -840,6 +846,15 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
840846
"first key is inconsistent with key range",
841847
));
842848
}
849+
850+
// The root node holds no value, so a ROOT-keyed item would panic in `put_raw`.
851+
// ROOT is the minimum key, so checking `items[0]` suffices given the sort check below.
852+
if chunk.items[0].key.is_empty() {
853+
return Err(MerkleRadixTrieError::InvalidChunk(
854+
"item key must not be the root key",
855+
));
856+
}
857+
843858
let last_item_key = chunk.items.last().unwrap().key.clone();
844859
if let Some(end_key) = &chunk.end_key
845860
&& last_item_key >= *end_key
@@ -2214,6 +2229,35 @@ mod tests {
22142229
);
22152230
}
22162231

2232+
#[test]
2233+
fn put_chunk_rejects_root_keyed_item() {
2234+
// A ROOT-keyed item used to reach `put_raw` and panic on
2235+
// `put_value(...).unwrap()`; `put_chunk` must reject it as `InvalidChunk`.
2236+
let env = MdbxDatabase::new_volatile(Default::default()).unwrap();
2237+
let trie = MerkleRadixTrie::new_incomplete(&env, TestTrie);
2238+
let mut raw_txn = env.write_transaction();
2239+
let mut txn: WriteTransactionProxy = (&mut raw_txn).into();
2240+
assert!(!trie.is_complete(&txn));
2241+
2242+
let proof_root = TrieNode::new_root();
2243+
let expected_hash = proof_root.hash_assert();
2244+
let proof = TrieProof::new(vec![proof_root.into()], Default::default());
2245+
2246+
let malicious_chunk = TrieChunk::new(
2247+
None,
2248+
vec![TrieItem::new(
2249+
KeyNibbles::ROOT,
2250+
vec![0xab, 0xbc, 0xcd, 0xde],
2251+
)],
2252+
proof,
2253+
);
2254+
2255+
match trie.put_chunk(&mut txn, KeyNibbles::ROOT, malicious_chunk, expected_hash) {
2256+
Err(MerkleRadixTrieError::InvalidChunk(_)) => {}
2257+
other => panic!("expected InvalidChunk(_), got {:?}", other),
2258+
}
2259+
}
2260+
22172261
#[test]
22182262
fn partial_tree_put_chunks_manual() {
22192263
let key_1 = "413f22".parse().unwrap();

0 commit comments

Comments
 (0)