From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Martin KaFai Lau <martin.lau@kernel.org>
Subject: [PATCH RFC bpf-next v1 1/2] bpf: Fix deadlock for bpf_timer's spinlock
Date: Sun, 6 Nov 2022 07:21:51 +0530 [thread overview]
Message-ID: <20221106015152.2556188-2-memxor@gmail.com> (raw)
In-Reply-To: <20221106015152.2556188-1-memxor@gmail.com>
Currently, unlike other tracing program types, BPF_PROG_TYPE_TRACING is
excluded is_tracing_prog_type checks. This means that they can use maps
containing bpf_spin_lock, bpf_timer, etc. without verification failure.
However, allowing fentry/fexit programs to use maps that have such
bpf_timer in the map value can lead to deadlock.
Suppose that an fentry program is attached to bpf_prog_put, and a TC
program executes and does bpf_map_update_elem on an array map that both
progs share. If the fentry programs calls bpf_map_update_elem for the
same key, it will lead to acquiring of the same lock from within the
critical section protecting the timer.
The call chain is:
bpf_prog_test_run_opts() // TC
bpf_prog_TC
bpf_map_update_elem(array_map, key=0)
bpf_obj_free_fields
bpf_timer_cancel_and_free
__bpf_spin_lock_irqsave
drop_prog_refcnt
bpf_prog_put
bpf_prog_FENTRY // FENTRY
bpf_map_update_elem(array_map, key=0)
bpf_obj_free_fields
bpf_timer_cancel_and_free
__bpf_spin_lock_irqsave // DEADLOCK
BPF_TRACE_ITER attach type can be excluded because it always executes in
process context.
Update selftests using bpf_timer in fentry to TC as they will be broken
by this change.
Fixes: 5e0bc3082e2e ("bpf: Forbid bpf_ktime_get_coarse_ns and bpf_timer_* in tracing progs")
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/verifier.c | 10 ++++++---
.../testing/selftests/bpf/prog_tests/timer.c | 21 ++++++++++++-------
.../selftests/bpf/prog_tests/timer_crash.c | 9 +++++++-
tools/testing/selftests/bpf/progs/timer.c | 8 +++----
.../testing/selftests/bpf/progs/timer_crash.c | 4 ++--
5 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d3b75aa0c54d..6e948c695e84 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12784,7 +12784,7 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
return err;
}
-static bool is_tracing_prog_type(enum bpf_prog_type type)
+static bool is_tracing_prog_type(enum bpf_prog_type type, enum bpf_attach_type eatype)
{
switch (type) {
case BPF_PROG_TYPE_KPROBE:
@@ -12792,6 +12792,9 @@ static bool is_tracing_prog_type(enum bpf_prog_type type)
case BPF_PROG_TYPE_PERF_EVENT:
case BPF_PROG_TYPE_RAW_TRACEPOINT:
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
+ case BPF_PROG_TYPE_TRACING:
+ if (eatype == BPF_TRACE_ITER)
+ return false;
return true;
default:
return false;
@@ -12803,6 +12806,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
struct bpf_prog *prog)
{
+ enum bpf_attach_type eatype = env->prog->expected_attach_type;
enum bpf_prog_type prog_type = resolve_prog_type(prog);
if (btf_record_has_field(map->record, BPF_SPIN_LOCK)) {
@@ -12811,7 +12815,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return -EINVAL;
}
- if (is_tracing_prog_type(prog_type)) {
+ if (is_tracing_prog_type(prog_type, eatype)) {
verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
return -EINVAL;
}
@@ -12823,7 +12827,7 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
}
if (btf_record_has_field(map->record, BPF_TIMER)) {
- if (is_tracing_prog_type(prog_type)) {
+ if (is_tracing_prog_type(prog_type, eatype)) {
verbose(env, "tracing progs cannot use bpf_timer yet\n");
return -EINVAL;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/timer.c b/tools/testing/selftests/bpf/prog_tests/timer.c
index 7eb049214859..c0c39da12250 100644
--- a/tools/testing/selftests/bpf/prog_tests/timer.c
+++ b/tools/testing/selftests/bpf/prog_tests/timer.c
@@ -1,25 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2021 Facebook */
#include <test_progs.h>
+#include <network_helpers.h>
#include "timer.skel.h"
static int timer(struct timer *timer_skel)
{
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = &pkt_v4,
+ .data_size_in = sizeof(pkt_v4),
+ .repeat = 1,
+ );
int err, prog_fd;
- LIBBPF_OPTS(bpf_test_run_opts, topts);
-
- err = timer__attach(timer_skel);
- if (!ASSERT_OK(err, "timer_attach"))
- return err;
ASSERT_EQ(timer_skel->data->callback_check, 52, "callback_check1");
ASSERT_EQ(timer_skel->data->callback2_check, 52, "callback2_check1");
prog_fd = bpf_program__fd(timer_skel->progs.test1);
err = bpf_prog_test_run_opts(prog_fd, &topts);
- ASSERT_OK(err, "test_run");
- ASSERT_EQ(topts.retval, 0, "test_run");
- timer__detach(timer_skel);
+ ASSERT_OK(err, "test_run test1");
+ ASSERT_EQ(topts.retval, 0, "test_run retval test1");
+
+ prog_fd = bpf_program__fd(timer_skel->progs.test2);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_run test2");
+ ASSERT_EQ(topts.retval, 0, "test_run retval test2");
usleep(50); /* 10 usecs should be enough, but give it extra */
/* check that timer_cb1() was executed 10+10 times */
diff --git a/tools/testing/selftests/bpf/prog_tests/timer_crash.c b/tools/testing/selftests/bpf/prog_tests/timer_crash.c
index f74b82305da8..91f2333b92aa 100644
--- a/tools/testing/selftests/bpf/prog_tests/timer_crash.c
+++ b/tools/testing/selftests/bpf/prog_tests/timer_crash.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
+#include <network_helpers.h>
#include "timer_crash.skel.h"
enum {
@@ -9,6 +10,11 @@ enum {
static void test_timer_crash_mode(int mode)
{
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = &pkt_v4,
+ .data_size_in = sizeof(pkt_v4),
+ .repeat = 1,
+ );
struct timer_crash *skel;
skel = timer_crash__open_and_load();
@@ -18,7 +24,8 @@ static void test_timer_crash_mode(int mode)
skel->bss->crash_map = mode;
if (!ASSERT_OK(timer_crash__attach(skel), "timer_crash__attach"))
goto end;
- usleep(1);
+ ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.timer), &topts), "test_run");
+ ASSERT_EQ(topts.retval, 0, "test_run retval");
end:
timer_crash__destroy(skel);
}
diff --git a/tools/testing/selftests/bpf/progs/timer.c b/tools/testing/selftests/bpf/progs/timer.c
index acda5c9cea93..492f62917898 100644
--- a/tools/testing/selftests/bpf/progs/timer.c
+++ b/tools/testing/selftests/bpf/progs/timer.c
@@ -119,8 +119,8 @@ static int timer_cb1(void *map, int *key, struct bpf_timer *timer)
return 0;
}
-SEC("fentry/bpf_fentry_test1")
-int BPF_PROG2(test1, int, a)
+SEC("tc")
+int test1(void *ctx)
{
struct bpf_timer *arr_timer, *lru_timer;
struct elem init = {};
@@ -235,8 +235,8 @@ int bpf_timer_test(void)
return 0;
}
-SEC("fentry/bpf_fentry_test2")
-int BPF_PROG2(test2, int, a, int, b)
+SEC("tc")
+int test2(void *ctx)
{
struct hmap_elem init = {}, *val;
int key = HTAB, key_malloc = HTAB_MALLOC;
diff --git a/tools/testing/selftests/bpf/progs/timer_crash.c b/tools/testing/selftests/bpf/progs/timer_crash.c
index f8f7944e70da..971eb93f485c 100644
--- a/tools/testing/selftests/bpf/progs/timer_crash.c
+++ b/tools/testing/selftests/bpf/progs/timer_crash.c
@@ -26,8 +26,8 @@ struct {
int pid = 0;
int crash_map = 0; /* 0 for amap, 1 for hmap */
-SEC("fentry/do_nanosleep")
-int sys_enter(void *ctx)
+SEC("tc")
+int timer(void *ctx)
{
struct map_elem *e, value = {};
void *map = crash_map ? (void *)&hmap : (void *)&amap;
--
2.38.1
next prev parent reply other threads:[~2022-11-06 1:52 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-06 1:51 [PATCH RFC bpf-next v1 0/2] Fix deadlock with bpf_timer in fentry/fexit progs Kumar Kartikeya Dwivedi
2022-11-06 1:51 ` Kumar Kartikeya Dwivedi [this message]
2022-11-06 21:20 ` [PATCH RFC bpf-next v1 1/2] bpf: Fix deadlock for bpf_timer's spinlock Alexei Starovoitov
2022-11-06 21:44 ` Kumar Kartikeya Dwivedi
2022-11-07 0:31 ` Alexei Starovoitov
2022-11-07 1:48 ` Kumar Kartikeya Dwivedi
2022-11-07 2:34 ` Alexei Starovoitov
2022-11-07 3:45 ` Alexei Starovoitov
2022-11-07 23:13 ` Kumar Kartikeya Dwivedi
2022-11-06 1:51 ` [PATCH RFC bpf-next v1 2/2] [EXAMPLE] selftests/bpf: Add timer deadlock example Kumar Kartikeya Dwivedi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221106015152.2556188-2-memxor@gmail.com \
--to=memxor@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=martin.lau@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox