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

selftests/bpf: add test for softlock when modifying hashmap while iterating

Add test that modifies the map while it's being iterated in such a way that
hangs the kernel thread unless the _safe fix is applied to
bpf_for_each_hash_elem.

Signed-off-by: Brandon Kammerdiener <brandon.kammerdiener@intel.com>
Link: https://lore.kernel.org/r/20250424153246.141677-3-brandon.kammerdiener@intel.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Hou Tao <houtao1@huawei.com>

authored by

Brandon Kammerdiener and committed by
Alexei Starovoitov
3d9c463f 75673fda

+67
+37
tools/testing/selftests/bpf/prog_tests/for_each.c
··· 6 6 #include "for_each_array_map_elem.skel.h" 7 7 #include "for_each_map_elem_write_key.skel.h" 8 8 #include "for_each_multi_maps.skel.h" 9 + #include "for_each_hash_modify.skel.h" 9 10 10 11 static unsigned int duration; 11 12 ··· 204 203 for_each_multi_maps__destroy(skel); 205 204 } 206 205 206 + static void test_hash_modify(void) 207 + { 208 + struct for_each_hash_modify *skel; 209 + int max_entries, i, err; 210 + __u64 key, val; 211 + 212 + LIBBPF_OPTS(bpf_test_run_opts, topts, 213 + .data_in = &pkt_v4, 214 + .data_size_in = sizeof(pkt_v4), 215 + .repeat = 1 216 + ); 217 + 218 + skel = for_each_hash_modify__open_and_load(); 219 + if (!ASSERT_OK_PTR(skel, "for_each_hash_modify__open_and_load")) 220 + return; 221 + 222 + max_entries = bpf_map__max_entries(skel->maps.hashmap); 223 + for (i = 0; i < max_entries; i++) { 224 + key = i; 225 + val = i; 226 + err = bpf_map__update_elem(skel->maps.hashmap, &key, sizeof(key), 227 + &val, sizeof(val), BPF_ANY); 228 + if (!ASSERT_OK(err, "map_update")) 229 + goto out; 230 + } 231 + 232 + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_pkt_access), &topts); 233 + ASSERT_OK(err, "bpf_prog_test_run_opts"); 234 + ASSERT_OK(topts.retval, "retval"); 235 + 236 + out: 237 + for_each_hash_modify__destroy(skel); 238 + } 239 + 207 240 void test_for_each(void) 208 241 { 209 242 if (test__start_subtest("hash_map")) ··· 248 213 test_write_map_key(); 249 214 if (test__start_subtest("multi_maps")) 250 215 test_multi_maps(); 216 + if (test__start_subtest("hash_modify")) 217 + test_hash_modify(); 251 218 }
+30
tools/testing/selftests/bpf/progs/for_each_hash_modify.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Intel Corporation */ 3 + #include "vmlinux.h" 4 + #include <bpf/bpf_helpers.h> 5 + 6 + char _license[] SEC("license") = "GPL"; 7 + 8 + struct { 9 + __uint(type, BPF_MAP_TYPE_HASH); 10 + __uint(max_entries, 128); 11 + __type(key, __u64); 12 + __type(value, __u64); 13 + } hashmap SEC(".maps"); 14 + 15 + static int cb(struct bpf_map *map, __u64 *key, __u64 *val, void *arg) 16 + { 17 + bpf_map_delete_elem(map, key); 18 + bpf_map_update_elem(map, key, val, 0); 19 + return 0; 20 + } 21 + 22 + SEC("tc") 23 + int test_pkt_access(struct __sk_buff *skb) 24 + { 25 + (void)skb; 26 + 27 + bpf_for_each_map_elem(&hashmap, cb, NULL, 0); 28 + 29 + return 0; 30 + }