Skip to content

Commit c9ae8c9

Browse files
author
Martin KaFai Lau
committed
Merge branch 'fixes for concurrent htab updates'
Hou Tao says: ==================== From: Hou Tao <houtao1@huawei.com> Hi, The patchset aims to fix the issues found during investigating the syzkaller problem reported in [0]. It seems that the concurrent updates to the same hash-table bucket may fail as shown in patch 1. Patch 1 uses preempt_disable() to fix the problem for htab_use_raw_lock() case. For !htab_use_raw_lock() case, the problem is left to "BPF specific memory allocator" patchset [1] in which !htab_use_raw_lock() will be removed. Patch 2 fixes the out-of-bound memory read problem reported in [0]. The problem has the root cause as patch 1 and it is fixed by handling -EBUSY from htab_lock_bucket() correctly. Patch 3 add two cases for hash-table update: one for the reentrancy of bpf_map_update_elem(), and another one for concurrent updates of the same hash-table bucket. Comments are always welcome. Regards, Tao [0]: https://lore.kernel.org/bpf/CACkBjsbuxaR6cv0kXJoVnBfL9ZJXjjoUcMpw_Ogc313jSrg14A@mail.gmail.com/ [1]: https://lore.kernel.org/bpf/20220819214232.18784-1-alexei.starovoitov@gmail.com/ Change Log: v4: * rebased on bpf-next * add htab_update to DENYLIST.s390x v3: https://lore.kernel.org/bpf/20220829023709.1958204-1-houtao@huaweicloud.com/ * patch 1: update commit message and add Fixes tag * patch 2: add Fixes tag * patch 3: elaborate the description of test cases v2: https://lore.kernel.org/bpf/bd60ef93-1c6a-2db2-557d-b09b92ad22bd@huaweicloud.com/ * Note the fix is for CONFIG_PREEMPT case in commit message and add Reviewed-by tag for patch 1 * Drop patch "bpf: Allow normally concurrent map updates for !htab_use_raw_lock() case" v1: https://lore.kernel.org/bpf/20220821033223.2598791-1-houtao@huaweicloud.com/ ==================== Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
2 parents 1970729 + 1c636b6 commit c9ae8c9

File tree

4 files changed

+179
-7
lines changed

4 files changed

+179
-7
lines changed

kernel/bpf/hashtab.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,25 @@ static inline int htab_lock_bucket(const struct bpf_htab *htab,
162162
unsigned long *pflags)
163163
{
164164
unsigned long flags;
165+
bool use_raw_lock;
165166

166167
hash = hash & HASHTAB_MAP_LOCK_MASK;
167168

168-
migrate_disable();
169+
use_raw_lock = htab_use_raw_lock(htab);
170+
if (use_raw_lock)
171+
preempt_disable();
172+
else
173+
migrate_disable();
169174
if (unlikely(__this_cpu_inc_return(*(htab->map_locked[hash])) != 1)) {
170175
__this_cpu_dec(*(htab->map_locked[hash]));
171-
migrate_enable();
176+
if (use_raw_lock)
177+
preempt_enable();
178+
else
179+
migrate_enable();
172180
return -EBUSY;
173181
}
174182

175-
if (htab_use_raw_lock(htab))
183+
if (use_raw_lock)
176184
raw_spin_lock_irqsave(&b->raw_lock, flags);
177185
else
178186
spin_lock_irqsave(&b->lock, flags);
@@ -185,13 +193,18 @@ static inline void htab_unlock_bucket(const struct bpf_htab *htab,
185193
struct bucket *b, u32 hash,
186194
unsigned long flags)
187195
{
196+
bool use_raw_lock = htab_use_raw_lock(htab);
197+
188198
hash = hash & HASHTAB_MAP_LOCK_MASK;
189-
if (htab_use_raw_lock(htab))
199+
if (use_raw_lock)
190200
raw_spin_unlock_irqrestore(&b->raw_lock, flags);
191201
else
192202
spin_unlock_irqrestore(&b->lock, flags);
193203
__this_cpu_dec(*(htab->map_locked[hash]));
194-
migrate_enable();
204+
if (use_raw_lock)
205+
preempt_enable();
206+
else
207+
migrate_enable();
195208
}
196209

197210
static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node);
@@ -1691,8 +1704,11 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map,
16911704
/* do not grab the lock unless need it (bucket_cnt > 0). */
16921705
if (locked) {
16931706
ret = htab_lock_bucket(htab, b, batch, &flags);
1694-
if (ret)
1695-
goto next_batch;
1707+
if (ret) {
1708+
rcu_read_unlock();
1709+
bpf_enable_instrumentation();
1710+
goto after_loop;
1711+
}
16961712
}
16971713

16981714
bucket_cnt = 0;

tools/testing/selftests/bpf/DENYLIST.s390x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@ unpriv_bpf_disabled # fentry
6868
setget_sockopt # attach unexpected error: -524 (trampoline)
6969
cb_refs # expected error message unexpected error: -524 (trampoline)
7070
cgroup_hierarchical_stats # JIT does not support calling kernel function (kfunc)
71+
htab_update # failed to attach: ERROR: strerror_r(-524)=22 (trampoline)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2022. Huawei Technologies Co., Ltd */
3+
#define _GNU_SOURCE
4+
#include <sched.h>
5+
#include <stdbool.h>
6+
#include <test_progs.h>
7+
#include "htab_update.skel.h"
8+
9+
struct htab_update_ctx {
10+
int fd;
11+
int loop;
12+
bool stop;
13+
};
14+
15+
static void test_reenter_update(void)
16+
{
17+
struct htab_update *skel;
18+
unsigned int key, value;
19+
int err;
20+
21+
skel = htab_update__open();
22+
if (!ASSERT_OK_PTR(skel, "htab_update__open"))
23+
return;
24+
25+
/* lookup_elem_raw() may be inlined and find_kernel_btf_id() will return -ESRCH */
26+
bpf_program__set_autoload(skel->progs.lookup_elem_raw, true);
27+
err = htab_update__load(skel);
28+
if (!ASSERT_TRUE(!err || err == -ESRCH, "htab_update__load") || err)
29+
goto out;
30+
31+
skel->bss->pid = getpid();
32+
err = htab_update__attach(skel);
33+
if (!ASSERT_OK(err, "htab_update__attach"))
34+
goto out;
35+
36+
/* Will trigger the reentrancy of bpf_map_update_elem() */
37+
key = 0;
38+
value = 0;
39+
err = bpf_map_update_elem(bpf_map__fd(skel->maps.htab), &key, &value, 0);
40+
if (!ASSERT_OK(err, "add element"))
41+
goto out;
42+
43+
ASSERT_EQ(skel->bss->update_err, -EBUSY, "no reentrancy");
44+
out:
45+
htab_update__destroy(skel);
46+
}
47+
48+
static void *htab_update_thread(void *arg)
49+
{
50+
struct htab_update_ctx *ctx = arg;
51+
cpu_set_t cpus;
52+
int i;
53+
54+
/* Pinned on CPU 0 */
55+
CPU_ZERO(&cpus);
56+
CPU_SET(0, &cpus);
57+
pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus);
58+
59+
i = 0;
60+
while (i++ < ctx->loop && !ctx->stop) {
61+
unsigned int key = 0, value = 0;
62+
int err;
63+
64+
err = bpf_map_update_elem(ctx->fd, &key, &value, 0);
65+
if (err) {
66+
ctx->stop = true;
67+
return (void *)(long)err;
68+
}
69+
}
70+
71+
return NULL;
72+
}
73+
74+
static void test_concurrent_update(void)
75+
{
76+
struct htab_update_ctx ctx;
77+
struct htab_update *skel;
78+
unsigned int i, nr;
79+
pthread_t *tids;
80+
int err;
81+
82+
skel = htab_update__open_and_load();
83+
if (!ASSERT_OK_PTR(skel, "htab_update__open_and_load"))
84+
return;
85+
86+
ctx.fd = bpf_map__fd(skel->maps.htab);
87+
ctx.loop = 1000;
88+
ctx.stop = false;
89+
90+
nr = 4;
91+
tids = calloc(nr, sizeof(*tids));
92+
if (!ASSERT_NEQ(tids, NULL, "no mem"))
93+
goto out;
94+
95+
for (i = 0; i < nr; i++) {
96+
err = pthread_create(&tids[i], NULL, htab_update_thread, &ctx);
97+
if (!ASSERT_OK(err, "pthread_create")) {
98+
unsigned int j;
99+
100+
ctx.stop = true;
101+
for (j = 0; j < i; j++)
102+
pthread_join(tids[j], NULL);
103+
goto out;
104+
}
105+
}
106+
107+
for (i = 0; i < nr; i++) {
108+
void *thread_err = NULL;
109+
110+
pthread_join(tids[i], &thread_err);
111+
ASSERT_EQ(thread_err, NULL, "update error");
112+
}
113+
114+
out:
115+
if (tids)
116+
free(tids);
117+
htab_update__destroy(skel);
118+
}
119+
120+
void test_htab_update(void)
121+
{
122+
if (test__start_subtest("reenter_update"))
123+
test_reenter_update();
124+
if (test__start_subtest("concurrent_update"))
125+
test_concurrent_update();
126+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (C) 2022. Huawei Technologies Co., Ltd */
3+
#include <linux/bpf.h>
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
struct {
10+
__uint(type, BPF_MAP_TYPE_HASH);
11+
__uint(max_entries, 1);
12+
__uint(key_size, sizeof(__u32));
13+
__uint(value_size, sizeof(__u32));
14+
} htab SEC(".maps");
15+
16+
int pid = 0;
17+
int update_err = 0;
18+
19+
SEC("?fentry/lookup_elem_raw")
20+
int lookup_elem_raw(void *ctx)
21+
{
22+
__u32 key = 0, value = 1;
23+
24+
if ((bpf_get_current_pid_tgid() >> 32) != pid)
25+
return 0;
26+
27+
update_err = bpf_map_update_elem(&htab, &key, &value, 0);
28+
return 0;
29+
}

0 commit comments

Comments
 (0)