Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add test for trie_get_next_key()

Add a test for out-of-bounds write in trie_get_next_key() when a full
path from root to leaf exists and bpf_map_get_next_key() is called
with the leaf node. It may crashes the kernel on failure, so please
run in a VM.

Signed-off-by: Byeonguk Jeong <jungbu2855@gmail.com>
Acked-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/Zxx4ep78tsbeWPVM@localhost.localdomain
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Byeonguk Jeong and committed by
Alexei Starovoitov
d7f214ae 13400ac8

+109
+109
tools/testing/selftests/bpf/map_tests/lpm_trie_map_get_next_key.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #define _GNU_SOURCE 4 + #include <linux/bpf.h> 5 + #include <stdio.h> 6 + #include <stdbool.h> 7 + #include <unistd.h> 8 + #include <errno.h> 9 + #include <stdlib.h> 10 + #include <string.h> 11 + #include <pthread.h> 12 + 13 + #include <bpf/bpf.h> 14 + #include <bpf/libbpf.h> 15 + 16 + #include <test_maps.h> 17 + 18 + struct test_lpm_key { 19 + __u32 prefix; 20 + __u32 data; 21 + }; 22 + 23 + struct get_next_key_ctx { 24 + struct test_lpm_key key; 25 + bool start; 26 + bool stop; 27 + int map_fd; 28 + int loop; 29 + }; 30 + 31 + static void *get_next_key_fn(void *arg) 32 + { 33 + struct get_next_key_ctx *ctx = arg; 34 + struct test_lpm_key next_key; 35 + int i = 0; 36 + 37 + while (!ctx->start) 38 + usleep(1); 39 + 40 + while (!ctx->stop && i++ < ctx->loop) 41 + bpf_map_get_next_key(ctx->map_fd, &ctx->key, &next_key); 42 + 43 + return NULL; 44 + } 45 + 46 + static void abort_get_next_key(struct get_next_key_ctx *ctx, pthread_t *tids, 47 + unsigned int nr) 48 + { 49 + unsigned int i; 50 + 51 + ctx->stop = true; 52 + ctx->start = true; 53 + for (i = 0; i < nr; i++) 54 + pthread_join(tids[i], NULL); 55 + } 56 + 57 + /* This test aims to prevent regression of future. As long as the kernel does 58 + * not panic, it is considered as success. 59 + */ 60 + void test_lpm_trie_map_get_next_key(void) 61 + { 62 + #define MAX_NR_THREADS 8 63 + LIBBPF_OPTS(bpf_map_create_opts, create_opts, 64 + .map_flags = BPF_F_NO_PREALLOC); 65 + struct test_lpm_key key = {}; 66 + __u32 val = 0; 67 + int map_fd; 68 + const __u32 max_prefixlen = 8 * (sizeof(key) - sizeof(key.prefix)); 69 + const __u32 max_entries = max_prefixlen + 1; 70 + unsigned int i, nr = MAX_NR_THREADS, loop = 65536; 71 + pthread_t tids[MAX_NR_THREADS]; 72 + struct get_next_key_ctx ctx; 73 + int err; 74 + 75 + map_fd = bpf_map_create(BPF_MAP_TYPE_LPM_TRIE, "lpm_trie_map", 76 + sizeof(struct test_lpm_key), sizeof(__u32), 77 + max_entries, &create_opts); 78 + CHECK(map_fd == -1, "bpf_map_create()", "error:%s\n", 79 + strerror(errno)); 80 + 81 + for (i = 0; i <= max_prefixlen; i++) { 82 + key.prefix = i; 83 + err = bpf_map_update_elem(map_fd, &key, &val, BPF_ANY); 84 + CHECK(err, "bpf_map_update_elem()", "error:%s\n", 85 + strerror(errno)); 86 + } 87 + 88 + ctx.start = false; 89 + ctx.stop = false; 90 + ctx.map_fd = map_fd; 91 + ctx.loop = loop; 92 + memcpy(&ctx.key, &key, sizeof(key)); 93 + 94 + for (i = 0; i < nr; i++) { 95 + err = pthread_create(&tids[i], NULL, get_next_key_fn, &ctx); 96 + if (err) { 97 + abort_get_next_key(&ctx, tids, i); 98 + CHECK(err, "pthread_create", "error %d\n", err); 99 + } 100 + } 101 + 102 + ctx.start = true; 103 + for (i = 0; i < nr; i++) 104 + pthread_join(tids[i], NULL); 105 + 106 + printf("%s:PASS\n", __func__); 107 + 108 + close(map_fd); 109 + }