Skip to content

Commit 5b036a3

Browse files
Hou TaoKernel Patches Daemon
Hou Tao
authored and
Kernel Patches Daemon
committed
selftests/bpf: Add test cases for inner map
Add test cases to test the race between the destroy of inner map due to map-in-map update and the access of inner map in bpf program. The following 4 combination are added: (1) array map in map array + bpf program (2) array map in map array + sleepable bpf program (3) array map in map htab + bpf program (4) array map in map htab + sleepable bpf program Before apply the fixes, when running "./test_prog -a map_in_map" with net.core.bpf_jit_enable=0, the following error was reported: BUG: KASAN: slab-use-after-free in bpf_map_lookup_elem+0x25/0x60 Read of size 8 at addr ffff888162fbe000 by task test_progs/3282 CPU: 4 PID: 3282 Comm: test_progs Not tainted 6.6.0-rc5+ #21 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) ...... Call Trace: <TASK> dump_stack_lvl+0x4b/0x80 print_report+0xcf/0x610 kasan_report+0x9d/0xd0 __asan_load8+0x7e/0xb0 bpf_map_lookup_elem+0x25/0x60 ___bpf_prog_run+0x2569/0x3c50 __bpf_prog_run32+0xa1/0xe0 trace_call_bpf+0x1a9/0x5e0 kprobe_perf_func+0xce/0x450 kprobe_dispatcher+0xa1/0xb0 kprobe_ftrace_handler+0x27b/0x370 0xffffffffc02080f7 RIP: 0010:__x64_sys_getpgid+0x1/0x30 ...... </TASK> Allocated by task 3281: kasan_save_stack+0x26/0x50 kasan_set_track+0x25/0x30 kasan_save_alloc_info+0x1b/0x30 __kasan_kmalloc+0x84/0xa0 __kmalloc_node+0x67/0x170 __bpf_map_area_alloc+0x13f/0x160 bpf_map_area_alloc+0x10/0x20 array_map_alloc+0x11d/0x2c0 map_create+0x285/0xc30 __sys_bpf+0xcff/0x3350 __x64_sys_bpf+0x45/0x60 do_syscall_64+0x33/0x60 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 1328: kasan_save_stack+0x26/0x50 kasan_set_track+0x25/0x30 kasan_save_free_info+0x2b/0x50 __kasan_slab_free+0x10f/0x1a0 __kmem_cache_free+0x1df/0x460 kfree+0x90/0x140 kvfree+0x2c/0x40 bpf_map_area_free+0xe/0x20 array_map_free+0x11f/0x270 bpf_map_free_deferred+0xda/0x200 process_scheduled_works+0x689/0xa20 worker_thread+0x2fd/0x5a0 kthread+0x1bf/0x200 ret_from_fork+0x39/0x70 ret_from_fork_asm+0x1b/0x30 Last potentially related work creation: kasan_save_stack+0x26/0x50 __kasan_record_aux_stack+0x92/0xa0 kasan_record_aux_stack_noalloc+0xb/0x20 insert_work+0x2a/0xc0 __queue_work+0x2a6/0x8d0 queue_work_on+0x7c/0x80 __bpf_map_put+0x103/0x140 bpf_map_put+0x10/0x20 bpf_map_fd_put_ptr+0x1e/0x30 bpf_fd_array_map_update_elem+0x18a/0x1d0 bpf_map_update_value+0x2ca/0x4b0 __sys_bpf+0x26ba/0x3350 __x64_sys_bpf+0x45/0x60 do_syscall_64+0x33/0x60 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Signed-off-by: Hou Tao <[email protected]>
1 parent 3e715cb commit 5b036a3

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2023. Huawei Technologies Co., Ltd */
3+
#define _GNU_SOURCE
4+
#include <unistd.h>
5+
#include <sys/syscall.h>
6+
#include <test_progs.h>
7+
#include <bpf/btf.h>
8+
#include "access_map_in_map.skel.h"
9+
10+
struct thread_ctx {
11+
pthread_barrier_t barrier;
12+
int outer_map_fd;
13+
int start, abort;
14+
int loop, err;
15+
};
16+
17+
static int wait_for_start_or_abort(struct thread_ctx *ctx)
18+
{
19+
while (!ctx->start && !ctx->abort)
20+
usleep(1);
21+
return ctx->abort ? -1 : 0;
22+
}
23+
24+
static void *update_map_fn(void *data)
25+
{
26+
struct thread_ctx *ctx = data;
27+
int loop = ctx->loop, err = 0;
28+
29+
if (wait_for_start_or_abort(ctx) < 0)
30+
return NULL;
31+
32+
while (loop-- > 0) {
33+
int fd, zero = 0;
34+
35+
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL);
36+
if (fd < 0) {
37+
err |= 1;
38+
pthread_barrier_wait(&ctx->barrier);
39+
continue;
40+
}
41+
42+
/* Remove the old inner map */
43+
if (bpf_map_update_elem(ctx->outer_map_fd, &zero, &fd, 0) < 0)
44+
err |= 2;
45+
close(fd);
46+
pthread_barrier_wait(&ctx->barrier);
47+
}
48+
49+
ctx->err = err;
50+
51+
return NULL;
52+
}
53+
54+
static void *access_map_fn(void *data)
55+
{
56+
struct thread_ctx *ctx = data;
57+
int loop = ctx->loop;
58+
59+
if (wait_for_start_or_abort(ctx) < 0)
60+
return NULL;
61+
62+
while (loop-- > 0) {
63+
/* Access the old inner map */
64+
syscall(SYS_getpgid);
65+
pthread_barrier_wait(&ctx->barrier);
66+
}
67+
68+
return NULL;
69+
}
70+
71+
static void test_map_in_map_access(const char *prog_name, const char *map_name)
72+
{
73+
struct access_map_in_map *skel;
74+
struct bpf_map *outer_map;
75+
struct bpf_program *prog;
76+
struct thread_ctx ctx;
77+
pthread_t tid[2];
78+
int err;
79+
80+
skel = access_map_in_map__open();
81+
if (!ASSERT_OK_PTR(skel, "access_map_in_map open"))
82+
return;
83+
84+
prog = bpf_object__find_program_by_name(skel->obj, prog_name);
85+
if (!ASSERT_OK_PTR(prog, "find program"))
86+
goto out;
87+
bpf_program__set_autoload(prog, true);
88+
89+
outer_map = bpf_object__find_map_by_name(skel->obj, map_name);
90+
if (!ASSERT_OK_PTR(outer_map, "find map"))
91+
goto out;
92+
93+
err = access_map_in_map__load(skel);
94+
if (!ASSERT_OK(err, "access_map_in_map load"))
95+
goto out;
96+
97+
err = access_map_in_map__attach(skel);
98+
if (!ASSERT_OK(err, "access_map_in_map attach"))
99+
goto out;
100+
101+
skel->bss->tgid = getpid();
102+
103+
memset(&ctx, 0, sizeof(ctx));
104+
pthread_barrier_init(&ctx.barrier, NULL, 2);
105+
ctx.outer_map_fd = bpf_map__fd(outer_map);
106+
ctx.loop = 8;
107+
108+
err = pthread_create(&tid[0], NULL, update_map_fn, &ctx);
109+
if (!ASSERT_OK(err, "close_thread"))
110+
goto out;
111+
112+
err = pthread_create(&tid[1], NULL, access_map_fn, &ctx);
113+
if (!ASSERT_OK(err, "read_thread")) {
114+
ctx.abort = 1;
115+
pthread_join(tid[0], NULL);
116+
goto out;
117+
}
118+
119+
ctx.start = 1;
120+
pthread_join(tid[0], NULL);
121+
pthread_join(tid[1], NULL);
122+
123+
ASSERT_OK(ctx.err, "err");
124+
out:
125+
access_map_in_map__destroy(skel);
126+
}
127+
128+
void test_map_in_map(void)
129+
{
130+
if (test__start_subtest("acc_map_in_array"))
131+
test_map_in_map_access("access_map_in_array", "outer_array_map");
132+
if (test__start_subtest("sleepable_acc_map_in_array"))
133+
test_map_in_map_access("sleepable_access_map_in_array", "outer_array_map");
134+
if (test__start_subtest("acc_map_in_htab"))
135+
test_map_in_map_access("access_map_in_htab", "outer_htab_map");
136+
if (test__start_subtest("sleepable_acc_map_in_htab"))
137+
test_map_in_map_access("sleepable_access_map_in_htab", "outer_htab_map");
138+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2023. Huawei Technologies Co., Ltd */
3+
#include <linux/bpf.h>
4+
#include <time.h>
5+
#include <bpf/bpf_helpers.h>
6+
7+
#include "bpf_misc.h"
8+
9+
struct inner_map_type {
10+
__uint(type, BPF_MAP_TYPE_ARRAY);
11+
__uint(key_size, 4);
12+
__uint(value_size, 4);
13+
__uint(max_entries, 1);
14+
} inner_map SEC(".maps");
15+
16+
struct {
17+
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
18+
__type(key, int);
19+
__type(value, int);
20+
__uint(max_entries, 1);
21+
__array(values, struct inner_map_type);
22+
} outer_array_map SEC(".maps") = {
23+
.values = {
24+
[0] = &inner_map,
25+
},
26+
};
27+
28+
struct {
29+
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
30+
__type(key, int);
31+
__type(value, int);
32+
__uint(max_entries, 1);
33+
__array(values, struct inner_map_type);
34+
} outer_htab_map SEC(".maps") = {
35+
.values = {
36+
[0] = &inner_map,
37+
},
38+
};
39+
40+
char _license[] SEC("license") = "GPL";
41+
42+
int tgid = 0;
43+
44+
static int acc_map_in_map(void *outer_map)
45+
{
46+
void *inner_map;
47+
int i, key;
48+
49+
if ((bpf_get_current_pid_tgid() >> 32) != tgid)
50+
return 0;
51+
52+
/* Find nonexistent inner map */
53+
key = 1;
54+
inner_map = bpf_map_lookup_elem(outer_map, &key);
55+
if (inner_map)
56+
return 0;
57+
58+
/* Find the old inner map */
59+
key = 0;
60+
inner_map = bpf_map_lookup_elem(outer_map, &key);
61+
if (!inner_map)
62+
return 0;
63+
64+
/* Wait for the old inner map to be replaced */
65+
for (i = 0; i < 256; i++) {
66+
int *ptr;
67+
68+
ptr = bpf_map_lookup_elem(inner_map, &key);
69+
if (!ptr)
70+
continue;
71+
*ptr = 0xdeadbeef;
72+
}
73+
74+
return 0;
75+
}
76+
77+
SEC("?kprobe/" SYS_PREFIX "sys_getpgid")
78+
int access_map_in_array(void *ctx)
79+
{
80+
return acc_map_in_map(&outer_array_map);
81+
}
82+
83+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
84+
int sleepable_access_map_in_array(void *ctx)
85+
{
86+
return acc_map_in_map(&outer_array_map);
87+
}
88+
89+
SEC("?kprobe/" SYS_PREFIX "sys_getpgid")
90+
int access_map_in_htab(void *ctx)
91+
{
92+
return acc_map_in_map(&outer_htab_map);
93+
}
94+
95+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
96+
int sleepable_access_map_in_htab(void *ctx)
97+
{
98+
return acc_map_in_map(&outer_htab_map);
99+
}

0 commit comments

Comments
 (0)