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

selftests/bpf: Add test for prealloc_lru_pop bug

Add a regression test to check against invalid check_and_init_map_value
call inside prealloc_lru_pop.

The kptr should not be reset to NULL once we set it after deleting the
map element. Hence, we trigger a program that updates the element
causing its reuse, and checks whether the unref kptr is reset or not.
If it is, prealloc_lru_pop does an incorrect check_and_init_map_value
call and the test fails.

Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20220809213033.24147-4-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
de7b9927 275c30bc

+70
+21
tools/testing/selftests/bpf/prog_tests/lru_bug.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <test_progs.h> 3 + 4 + #include "lru_bug.skel.h" 5 + 6 + void test_lru_bug(void) 7 + { 8 + struct lru_bug *skel; 9 + int ret; 10 + 11 + skel = lru_bug__open_and_load(); 12 + if (!ASSERT_OK_PTR(skel, "lru_bug__open_and_load")) 13 + return; 14 + ret = lru_bug__attach(skel); 15 + if (!ASSERT_OK(ret, "lru_bug__attach")) 16 + goto end; 17 + usleep(1); 18 + ASSERT_OK(skel->data->result, "prealloc_lru_pop doesn't call check_and_init_map_value"); 19 + end: 20 + lru_bug__destroy(skel); 21 + }
+49
tools/testing/selftests/bpf/progs/lru_bug.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <vmlinux.h> 3 + #include <bpf/bpf_tracing.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct map_value { 7 + struct task_struct __kptr *ptr; 8 + }; 9 + 10 + struct { 11 + __uint(type, BPF_MAP_TYPE_LRU_HASH); 12 + __uint(max_entries, 1); 13 + __type(key, int); 14 + __type(value, struct map_value); 15 + } lru_map SEC(".maps"); 16 + 17 + int pid = 0; 18 + int result = 1; 19 + 20 + SEC("fentry/bpf_ktime_get_ns") 21 + int printk(void *ctx) 22 + { 23 + struct map_value v = {}; 24 + 25 + if (pid == bpf_get_current_task_btf()->pid) 26 + bpf_map_update_elem(&lru_map, &(int){0}, &v, 0); 27 + return 0; 28 + } 29 + 30 + SEC("fentry/do_nanosleep") 31 + int nanosleep(void *ctx) 32 + { 33 + struct map_value val = {}, *v; 34 + struct task_struct *current; 35 + 36 + bpf_map_update_elem(&lru_map, &(int){0}, &val, 0); 37 + v = bpf_map_lookup_elem(&lru_map, &(int){0}); 38 + if (!v) 39 + return 0; 40 + bpf_map_delete_elem(&lru_map, &(int){0}); 41 + current = bpf_get_current_task_btf(); 42 + v->ptr = current; 43 + pid = current->pid; 44 + bpf_ktime_get_ns(); 45 + result = !v->ptr; 46 + return 0; 47 + } 48 + 49 + char _license[] SEC("license") = "GPL";