trie_get_next_key() uses node->prefixlen == key->prefixlen to identify an exact match, However, it is incorrect because when the target key doesn't fully match the found node (e.g., node->prefixlen != matchlen), these two nodes may also have the same prefixlen. It will return expected result when the passed key exist in the trie. However when a recently-deleted key or nonexistent key is passed to trie_get_next_key(), it may skip keys and return incorrect result. Fix it by using node->prefixlen == matchlen to identify exact matches. When the condition is true after the search, it also implies node->prefixlen equals key->prefixlen, otherwise, the search would return NULL instead.
Unified · Split
| @@ -645,7 +645,7 @@ static int trie_get_next_key(struct bpf_map *map, void *_key, void *_next_key) · 644 unmodified lines | ||
| 645 | 645 | struct lpm_trie_node **node_stack = NULL; |
| 646 | 646 | int err = 0, stack_ptr = -1; |
| 647 | 647 | unsigned int next_bit; |
| 648 | − size_t matchlen; | |
| 648 | + size_t matchlen = 0; | |
| 649 | 649 | |
| 650 | 650 | /* The get_next_key follows postorder. For the 4 node example in |
| 651 | 651 | * the top of this file, the trie_get_next_key() returns the following |
| @@ -684,7 +684,7 @@ static int trie_get_next_key(struct bpf_map *map, void *_key, void *_next_key) · 32 unmodified lines | ||
| 684 | 684 | next_bit = extract_bit(key->data, node->prefixlen); |
| 685 | 685 | node = rcu_dereference(node->child[next_bit]); |
| 686 | 686 | } |
| 687 | − if (!node || node->prefixlen != key->prefixlen || | |
| 687 | + if (!node || node->prefixlen != matchlen || | |
| 688 | 688 | (node->flags & LPM_TREE_NODE_FLAG_IM)) |
| 689 | 689 | goto find_leftmost; |
| 690 | 690 | |