]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: lpm_trie: check left child of last leftmost node for NULL
authorJonathan Lemon <jonathan.lemon@gmail.com>
Sat, 8 Jun 2019 19:54:19 +0000 (12:54 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Jul 2019 11:13:43 +0000 (13:13 +0200)
commit da2577fdd0932ea4eefe73903f1130ee366767d2 upstream.

If the leftmost parent node of the tree has does not have a child
on the left side, then trie_get_next_key (and bpftool map dump) will
not look at the child on the right.  This leads to the traversal
missing elements.

Lookup is not affected.

Update selftest to handle this case.

Reproducer:

 bpftool map create /sys/fs/bpf/lpm type lpm_trie key 6 \
     value 1 entries 256 name test_lpm flags 1
 bpftool map update pinned /sys/fs/bpf/lpm key  8 0 0 0  0   0 value 1
 bpftool map update pinned /sys/fs/bpf/lpm key 16 0 0 0  0 128 value 2
 bpftool map dump   pinned /sys/fs/bpf/lpm

Returns only 1 element. (2 expected)

Fixes: b471f2f1de8b ("bpf: implement MAP_GET_NEXT_KEY command for LPM_TRIE")
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
kernel/bpf/lpm_trie.c
tools/testing/selftests/bpf/test_lpm_map.c

index 93a5cbbde421c346e72b10cd56e04a13888d9e7b..3b03a7342f3c26f1bf72581d5ee625cfca6074c9 100644 (file)
@@ -715,9 +715,14 @@ find_leftmost:
         * have exact two children, so this function will never return NULL.
         */
        for (node = search_root; node;) {
-               if (!(node->flags & LPM_TREE_NODE_FLAG_IM))
+               if (node->flags & LPM_TREE_NODE_FLAG_IM) {
+                       node = rcu_dereference(node->child[0]);
+               } else {
                        next_node = node;
-               node = rcu_dereference(node->child[0]);
+                       node = rcu_dereference(node->child[0]);
+                       if (!node)
+                               node = rcu_dereference(next_node->child[1]);
+               }
        }
 do_copy:
        next_key->prefixlen = next_node->prefixlen;
index 02d7c871862af26080e823a6936a54bbd182e454..006be39639778f9311fd849849a41ebe4e62f5c0 100644 (file)
@@ -573,13 +573,13 @@ static void test_lpm_get_next_key(void)
 
        /* add one more element (total two) */
        key_p->prefixlen = 24;
-       inet_pton(AF_INET, "192.168.0.0", key_p->data);
+       inet_pton(AF_INET, "192.168.128.0", key_p->data);
        assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
 
        memset(key_p, 0, key_size);
        assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
        assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
-              key_p->data[1] == 168 && key_p->data[2] == 0);
+              key_p->data[1] == 168 && key_p->data[2] == 128);
 
        memset(next_key_p, 0, key_size);
        assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
@@ -592,7 +592,7 @@ static void test_lpm_get_next_key(void)
 
        /* Add one more element (total three) */
        key_p->prefixlen = 24;
-       inet_pton(AF_INET, "192.168.128.0", key_p->data);
+       inet_pton(AF_INET, "192.168.0.0", key_p->data);
        assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
 
        memset(key_p, 0, key_size);
@@ -643,6 +643,41 @@ static void test_lpm_get_next_key(void)
        assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
               errno == ENOENT);
 
+       /* Add one more element (total five) */
+       key_p->prefixlen = 28;
+       inet_pton(AF_INET, "192.168.1.128", key_p->data);
+       assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
+
+       memset(key_p, 0, key_size);
+       assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
+       assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
+              key_p->data[1] == 168 && key_p->data[2] == 0);
+
+       memset(next_key_p, 0, key_size);
+       assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+       assert(next_key_p->prefixlen == 28 && next_key_p->data[0] == 192 &&
+              next_key_p->data[1] == 168 && next_key_p->data[2] == 1 &&
+              next_key_p->data[3] == 128);
+
+       memcpy(key_p, next_key_p, key_size);
+       assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+       assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
+              next_key_p->data[1] == 168 && next_key_p->data[2] == 1);
+
+       memcpy(key_p, next_key_p, key_size);
+       assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+       assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
+              next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
+
+       memcpy(key_p, next_key_p, key_size);
+       assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+       assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
+              next_key_p->data[1] == 168);
+
+       memcpy(key_p, next_key_p, key_size);
+       assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
+              errno == ENOENT);
+
        /* no exact matching key should return the first one in post order */
        key_p->prefixlen = 22;
        inet_pton(AF_INET, "192.168.1.0", key_p->data);