* [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace
@ 2024-09-29 13:27 Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map Leon Hwang
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Leon Hwang @ 2024-09-29 13:27 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, leon.hwang, kernel-patches-bot
Previously, I fixed a tailcall infinite loop issue caused by trampoline[0].
At this time, I fix a tailcall infinite loop issue caused by tailcall and
freplace combination by preventing updating extended prog to prog_array map
and preventing extending tail callee prog with freplace:
1. If a prog or its subprog has been extended by freplace prog, the prog
can not be updated to prog_array map.
2. If a prog has been updated to prog_array map, it or its subprog can not
be extended by freplace prog.
v3 -> v4:
* Address comments from Eduard:
* Rename 'tail_callee_cnt' to 'prog_array_member_cnt'.
* Add comment to 'prog_array_member_cnt'.
* Use a mutex to protect 'is_extended' and 'prog_array_member_cnt'.
v2 -> v3:
* Address comments from Alexei:
* Stop hacking JIT.
* Prevent the specific use case at attach/update time.
v1 -> v2:
* Address comment from Eduard:
* Explain why nop5 and xor/nop3 are swapped at prologue.
* Address comment from Alexei:
* Disallow attaching tail_call_reachable freplace prog to
not-tail_call_reachable target in verifier.
* Update "bpf, arm64: Fix tailcall infinite loop caused by freplace" with
latest arm64 JIT code.
Links:
[0] https://lore.kernel.org/bpf/20230912150442.2009-1-hffilwlqm@gmail.com/
Leon Hwang (4):
bpf: Prevent updating extended prog to prog_array map
bpf: Prevent extending tail callee prog with freplace prog
selftests/bpf: Add a test case to confirm a tailcall infinite loop
issue has been prevented
selftests/bpf: Add cases to test tailcall in freplace
include/linux/bpf.h | 3 +
kernel/bpf/arraymap.c | 30 ++-
kernel/bpf/core.c | 1 +
kernel/bpf/syscall.c | 53 ++++-
.../selftests/bpf/prog_tests/tailcalls.c | 196 +++++++++++++++++-
.../tailcall_bpf2bpf_hierarchy_freplace.c | 30 +++
.../testing/selftests/bpf/progs/tc_bpf2bpf.c | 37 +++-
7 files changed, 335 insertions(+), 15 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_freplace.c
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
@ 2024-09-29 13:27 ` Leon Hwang
2024-10-01 11:13 ` Eduard Zingerman
2024-09-29 13:27 ` [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog Leon Hwang
` (3 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Leon Hwang @ 2024-09-29 13:27 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, leon.hwang, kernel-patches-bot
This patch partially prevents a potential infinite loop issue caused by
combination of tailcal and freplace.
For example:
tc_bpf2bpf.c:
// SPDX-License-Identifier: GPL-2.0
\#include <linux/bpf.h>
\#include <bpf/bpf_helpers.h>
__noinline
int subprog_tc(struct __sk_buff *skb)
{
return skb->len * 2;
}
SEC("tc")
int entry_tc(struct __sk_buff *skb)
{
return subprog_tc(skb);
}
char __license[] SEC("license") = "GPL";
tailcall_freplace.c:
// SPDX-License-Identifier: GPL-2.0
\#include <linux/bpf.h>
\#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} jmp_table SEC(".maps");
int count = 0;
SEC("freplace")
int entry_freplace(struct __sk_buff *skb)
{
count++;
bpf_tail_call_static(skb, &jmp_table, 0);
return count;
}
char __license[] SEC("license") = "GPL";
The attach target of entry_freplace is subprog_tc, and the tail callee
in entry_freplace is entry_tc.
Then, the infinite loop will be entry_tc -> subprog_tc -> entry_freplace
--tailcall-> entry_tc, because tail_call_cnt in entry_freplace will count
from zero for every time of entry_freplace execution. Kernel will panic,
like:
[ 15.310490] BUG: TASK stack guard page was hit at (____ptrval____)
(stack is (____ptrval____)..(____ptrval____))
[ 15.310490] Oops: stack guard page: 0000 [#1] PREEMPT SMP NOPTI
[ 15.310490] CPU: 1 PID: 89 Comm: test_progs Tainted: G OE
6.10.0-rc6-g026dcdae8d3e-dirty #72
[ 15.310490] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 15.310490] RIP: 0010:bpf_prog_3a140cef239a4b4f_subprog_tail+0x14/0x53
[ 15.310490] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc f3 0f 1e fa 0f 1f 44 00 00 0f 1f 00 55 48 89 e5 f3 0f 1e
fa <50> 50 53 41 55 48 89 fb 49 bd 00 2a 46 82 98 9c ff ff 48 89 df 4c
[ 15.310490] RSP: 0018:ffffb500c0aa0000 EFLAGS: 00000202
[ 15.310490] RAX: ffffb500c0aa0028 RBX: ffff9c98808b7e00 RCX:
0000000000008cb5
[ 15.310490] RDX: 0000000000000000 RSI: ffff9c9882462a00 RDI:
ffff9c98808b7e00
[ 15.310490] RBP: ffffb500c0aa0000 R08: 0000000000000000 R09:
0000000000000000
[ 15.310490] R10: 0000000000000001 R11: 0000000000000000 R12:
ffffb500c01af000
[ 15.310490] R13: ffffb500c01cd000 R14: 0000000000000000 R15:
0000000000000000
[ 15.310490] FS: 00007f133b665140(0000) GS:ffff9c98bbd00000(0000)
knlGS:0000000000000000
[ 15.310490] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 15.310490] CR2: ffffb500c0a9fff8 CR3: 0000000102478000 CR4:
00000000000006f0
[ 15.310490] Call Trace:
[ 15.310490] <#DF>
[ 15.310490] ? die+0x36/0x90
[ 15.310490] ? handle_stack_overflow+0x4d/0x60
[ 15.310490] ? exc_double_fault+0x117/0x1a0
[ 15.310490] ? asm_exc_double_fault+0x23/0x30
[ 15.310490] ? bpf_prog_3a140cef239a4b4f_subprog_tail+0x14/0x53
[ 15.310490] </#DF>
[ 15.310490] <TASK>
[ 15.310490] bpf_prog_85781a698094722f_entry+0x4c/0x64
[ 15.310490] bpf_prog_1c515f389a9059b4_entry2+0x19/0x1b
[ 15.310490] ...
[ 15.310490] bpf_prog_85781a698094722f_entry+0x4c/0x64
[ 15.310490] bpf_prog_1c515f389a9059b4_entry2+0x19/0x1b
[ 15.310490] bpf_test_run+0x210/0x370
[ 15.310490] ? bpf_test_run+0x128/0x370
[ 15.310490] bpf_prog_test_run_skb+0x388/0x7a0
[ 15.310490] __sys_bpf+0xdbf/0x2c40
[ 15.310490] ? clockevents_program_event+0x52/0xf0
[ 15.310490] ? lock_release+0xbf/0x290
[ 15.310490] __x64_sys_bpf+0x1e/0x30
[ 15.310490] do_syscall_64+0x68/0x140
[ 15.310490] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 15.310490] RIP: 0033:0x7f133b52725d
[ 15.310490] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa
48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f
05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 8b bb 0d 00 f7 d8 64 89 01 48
[ 15.310490] RSP: 002b:00007ffddbc10258 EFLAGS: 00000206 ORIG_RAX:
0000000000000141
[ 15.310490] RAX: ffffffffffffffda RBX: 00007ffddbc10828 RCX:
00007f133b52725d
[ 15.310490] RDX: 0000000000000050 RSI: 00007ffddbc102a0 RDI:
000000000000000a
[ 15.310490] RBP: 00007ffddbc10270 R08: 0000000000000000 R09:
00007ffddbc102a0
[ 15.310490] R10: 0000000000000064 R11: 0000000000000206 R12:
0000000000000004
[ 15.310490] R13: 0000000000000000 R14: 0000558ec4c24890 R15:
00007f133b6ed000
[ 15.310490] </TASK>
[ 15.310490] Modules linked in: bpf_testmod(OE)
[ 15.310490] ---[ end trace 0000000000000000 ]---
[ 15.310490] RIP: 0010:bpf_prog_3a140cef239a4b4f_subprog_tail+0x14/0x53
[ 15.310490] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc f3 0f 1e fa 0f 1f 44 00 00 0f 1f 00 55 48 89 e5 f3 0f 1e
fa <50> 50 53 41 55 48 89 fb 49 bd 00 2a 46 82 98 9c ff ff 48 89 df 4c
[ 15.310490] RSP: 0018:ffffb500c0aa0000 EFLAGS: 00000202
[ 15.310490] RAX: ffffb500c0aa0028 RBX: ffff9c98808b7e00 RCX:
0000000000008cb5
[ 15.310490] RDX: 0000000000000000 RSI: ffff9c9882462a00 RDI:
ffff9c98808b7e00
[ 15.310490] RBP: ffffb500c0aa0000 R08: 0000000000000000 R09:
0000000000000000
[ 15.310490] R10: 0000000000000001 R11: 0000000000000000 R12:
ffffb500c01af000
[ 15.310490] R13: ffffb500c01cd000 R14: 0000000000000000 R15:
0000000000000000
[ 15.310490] FS: 00007f133b665140(0000) GS:ffff9c98bbd00000(0000)
knlGS:0000000000000000
[ 15.310490] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 15.310490] CR2: ffffb500c0a9fff8 CR3: 0000000102478000 CR4:
00000000000006f0
[ 15.310490] Kernel panic - not syncing: Fatal exception in interrupt
[ 15.310490] Kernel Offset: 0x30000000 from 0xffffffff81000000
(relocation range: 0xffffffff80000000-0xffffffffbfffffff)
This patch partially prevents this panic by preventing updating extended
prog to prog_array map.
If a prog or its subprog has been extended by freplace prog, the prog
can not be updated to prog_array map.
Alongside next patch, the panic will be prevented completely.
BTW, fix a minor style issue by replacing 8-spaces with a tab.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf.h | 2 ++
kernel/bpf/arraymap.c | 21 +++++++++++++++++----
kernel/bpf/core.c | 1 +
kernel/bpf/syscall.c | 42 ++++++++++++++++++++++++++++++++++++------
4 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 19d8ca8ac960f..aac6d2f42830c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1483,6 +1483,8 @@ struct bpf_prog_aux {
bool xdp_has_frags;
bool exception_cb;
bool exception_boundary;
+ bool is_extended; /* true if extended by freplace program */
+ struct mutex ext_mutex; /* mutex for is_extended */
struct bpf_arena *arena;
/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
const struct btf_type *attach_func_proto;
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 79660e3fca4c1..4a4de4f014be9 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -947,16 +947,29 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
struct file *map_file, int fd)
{
struct bpf_prog *prog = bpf_prog_get(fd);
+ bool is_extended;
if (IS_ERR(prog))
return prog;
- if (!bpf_prog_map_compatible(map, prog)) {
- bpf_prog_put(prog);
- return ERR_PTR(-EINVAL);
- }
+ if (!bpf_prog_map_compatible(map, prog))
+ goto out_put_prog;
+
+ mutex_lock(&prog->aux->ext_mutex);
+ is_extended = prog->aux->is_extended;
+ mutex_unlock(&prog->aux->ext_mutex);
+ if (is_extended)
+ /* Extended prog can not be tail callee. It's to prevent a
+ * potential infinite loop like:
+ * tail callee prog entry -> tail callee prog subprog ->
+ * freplace prog entry --tailcall-> tail callee prog entry.
+ */
+ goto out_put_prog;
return prog;
+out_put_prog:
+ bpf_prog_put(prog);
+ return ERR_PTR(-EINVAL);
}
static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 4e07cc057d6f2..ea7f59374b378 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -131,6 +131,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
INIT_LIST_HEAD_RCU(&fp->aux->ksym_prefix.lnode);
#endif
mutex_init(&fp->aux->used_maps_mutex);
+ mutex_init(&fp->aux->ext_mutex);
mutex_init(&fp->aux->dst_mutex);
return fp;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a8f1808a1ca54..db17c52fa35db 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3212,14 +3212,23 @@ static void bpf_tracing_link_release(struct bpf_link *link)
{
struct bpf_tracing_link *tr_link =
container_of(link, struct bpf_tracing_link, link.link);
-
- WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
- tr_link->trampoline));
+ struct bpf_prog *tgt_prog = tr_link->tgt_prog;
+
+ if (link->prog->type == BPF_PROG_TYPE_EXT) {
+ mutex_lock(&tgt_prog->aux->ext_mutex);
+ WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
+ tr_link->trampoline));
+ tgt_prog->aux->is_extended = false;
+ mutex_unlock(&tgt_prog->aux->ext_mutex);
+ } else {
+ WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
+ tr_link->trampoline));
+ }
bpf_trampoline_put(tr_link->trampoline);
/* tgt_prog is NULL if target is a kernel function */
- if (tr_link->tgt_prog)
+ if (tgt_prog)
bpf_prog_put(tr_link->tgt_prog);
}
@@ -3270,6 +3279,24 @@ static const struct bpf_link_ops bpf_tracing_link_lops = {
.fill_link_info = bpf_tracing_link_fill_link_info,
};
+static int bpf_extend_prog(struct bpf_tracing_link *link,
+ struct bpf_trampoline *tr,
+ struct bpf_prog *tgt_prog)
+{
+ struct bpf_prog_aux *aux = tgt_prog->aux;
+ int err = 0;
+
+ mutex_lock(&aux->ext_mutex);
+ err = bpf_trampoline_link_prog(&link->link, tr);
+ if (err)
+ goto out_unlock;
+
+ aux->is_extended = true;
+out_unlock:
+ mutex_unlock(&aux->ext_mutex);
+ return err;
+}
+
static int bpf_tracing_prog_attach(struct bpf_prog *prog,
int tgt_prog_fd,
u32 btf_id,
@@ -3354,7 +3381,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
* in prog->aux
*
* - if prog->aux->dst_trampoline is NULL, the program has already been
- * attached to a target and its initial target was cleared (below)
+ * attached to a target and its initial target was cleared (below)
*
* - if tgt_prog != NULL, the caller specified tgt_prog_fd +
* target_btf_id using the link_create API.
@@ -3429,7 +3456,10 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
if (err)
goto out_unlock;
- err = bpf_trampoline_link_prog(&link->link, tr);
+ if (prog->type == BPF_PROG_TYPE_EXT)
+ err = bpf_extend_prog(link, tr, tgt_prog);
+ else
+ err = bpf_trampoline_link_prog(&link->link, tr);
if (err) {
bpf_link_cleanup(&link_primer);
link = NULL;
--
2.44.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map Leon Hwang
@ 2024-09-29 13:27 ` Leon Hwang
2024-09-30 1:53 ` Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 3/4] selftests/bpf: Add a test case to confirm a tailcall infinite loop issue has been prevented Leon Hwang
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Leon Hwang @ 2024-09-29 13:27 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, leon.hwang, kernel-patches-bot
Alongside previous patch, the infinite loop issue caused by combination of
tailcal and freplace can be prevented completely.
The previous patch can not prevent the use case that updates a prog to
prog_array map and then extends subprog of the prog with freplace prog.
This patch fixes the case by preventing extending a prog, which has been
updated to prog_array map, with freplace prog.
If a prog has been updated to prog_array map, it or its subprog can not
be extended by freplace prog.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf.h | 3 ++-
kernel/bpf/arraymap.c | 9 ++++++++-
kernel/bpf/syscall.c | 11 +++++++++++
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index aac6d2f42830c..dc19ad99e2857 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1484,7 +1484,8 @@ struct bpf_prog_aux {
bool exception_cb;
bool exception_boundary;
bool is_extended; /* true if extended by freplace program */
- struct mutex ext_mutex; /* mutex for is_extended */
+ u32 prog_array_member_cnt; /* counts how many times as member of prog_array */
+ struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
struct bpf_arena *arena;
/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
const struct btf_type *attach_func_proto;
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 4a4de4f014be9..91b5bdf4dc72d 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -957,6 +957,8 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
mutex_lock(&prog->aux->ext_mutex);
is_extended = prog->aux->is_extended;
+ if (!is_extended)
+ prog->aux->prog_array_member_cnt++;
mutex_unlock(&prog->aux->ext_mutex);
if (is_extended)
/* Extended prog can not be tail callee. It's to prevent a
@@ -974,8 +976,13 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
{
+ struct bpf_prog *prog = ptr;
+
+ mutex_lock(&prog->aux->ext_mutex);
+ prog->aux->prog_array_member_cnt--;
+ mutex_unlock(&prog->aux->ext_mutex);
/* bpf_prog is freed after one RCU or tasks trace grace period */
- bpf_prog_put(ptr);
+ bpf_prog_put(prog);
}
static u32 prog_fd_array_sys_lookup_elem(void *ptr)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index db17c52fa35db..4beec9729f742 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3287,6 +3287,17 @@ static int bpf_extend_prog(struct bpf_tracing_link *link,
int err = 0;
mutex_lock(&aux->ext_mutex);
+ if (aux->prog_array_member_cnt) {
+ /* Program extensions can not extend target prog when the target
+ * prog has been updated to any prog_array map as tail callee.
+ * It's to prevent a potential infinite loop like:
+ * tgt prog entry -> tgt prog subprog -> freplace prog entry
+ * --tailcall-> tgt prog entry.
+ */
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
err = bpf_trampoline_link_prog(&link->link, tr);
if (err)
goto out_unlock;
--
2.44.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH bpf-next v4 3/4] selftests/bpf: Add a test case to confirm a tailcall infinite loop issue has been prevented
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog Leon Hwang
@ 2024-09-29 13:27 ` Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 4/4] selftests/bpf: Add cases to test tailcall in freplace Leon Hwang
2024-10-01 11:14 ` [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Eduard Zingerman
4 siblings, 0 replies; 14+ messages in thread
From: Leon Hwang @ 2024-09-29 13:27 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, leon.hwang, kernel-patches-bot
cd tools/testing/selftests/bpf; ./test_progs -t tailcalls
335/26 tailcalls/tailcall_bpf2bpf_freplace:OK
335 tailcalls:OK
Summary: 1/26 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/tailcalls.c | 99 ++++++++++++++++++-
.../testing/selftests/bpf/progs/tc_bpf2bpf.c | 24 ++++-
2 files changed, 119 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index 21c5a37846ade..fa3f3bb11b098 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -1525,7 +1525,8 @@ static void test_tailcall_freplace(void)
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
freplace_prog = freplace_skel->progs.entry_freplace;
- err = bpf_program__set_attach_target(freplace_prog, prog_fd, "subprog");
+ err = bpf_program__set_attach_target(freplace_prog, prog_fd,
+ "subprog_tailcall_tc");
if (!ASSERT_OK(err, "set_attach_target"))
goto out;
@@ -1534,7 +1535,7 @@ static void test_tailcall_freplace(void)
goto out;
freplace_link = bpf_program__attach_freplace(freplace_prog, prog_fd,
- "subprog");
+ "subprog_tailcall_tc");
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
goto out;
@@ -1556,6 +1557,98 @@ static void test_tailcall_freplace(void)
tailcall_freplace__destroy(freplace_skel);
}
+/* test_tailcall_bpf2bpf_freplace checks the failure that fails to attach a tail
+ * callee prog with freplace prog or fails to update an extended prog to
+ * prog_array map.
+ */
+static void test_tailcall_bpf2bpf_freplace(void)
+{
+ struct tailcall_freplace *freplace_skel = NULL;
+ struct bpf_link *freplace_link = NULL;
+ struct tc_bpf2bpf *tc_skel = NULL;
+ char buff[128] = {};
+ int prog_fd, map_fd;
+ int err, key;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = buff,
+ .data_size_in = sizeof(buff),
+ .repeat = 1,
+ );
+
+ tc_skel = tc_bpf2bpf__open_and_load();
+ if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
+ goto out;
+
+ prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
+ freplace_skel = tailcall_freplace__open();
+ if (!ASSERT_OK_PTR(freplace_skel, "tailcall_freplace__open"))
+ goto out;
+
+ err = bpf_program__set_attach_target(freplace_skel->progs.entry_freplace,
+ prog_fd, "subprog_tc");
+ if (!ASSERT_OK(err, "set_attach_target"))
+ goto out;
+
+ err = tailcall_freplace__load(freplace_skel);
+ if (!ASSERT_OK(err, "tailcall_freplace__load"))
+ goto out;
+
+ /* OK to attach then detach freplace prog. */
+
+ freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
+ prog_fd, "subprog_tc");
+ if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
+ goto out;
+
+ err = bpf_link__destroy(freplace_link);
+ if (!ASSERT_OK(err, "destroy link"))
+ goto out;
+
+ /* OK to update prog_array map then delete element from the map. */
+
+ key = 0;
+ map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
+ err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
+ if (!ASSERT_OK(err, "update jmp_table"))
+ goto out;
+
+ err = bpf_map_delete_elem(map_fd, &key);
+ if (!ASSERT_OK(err, "delete_elem from jmp_table"))
+ goto out;
+
+ /* Fail to attach a tail callee prog with freplace prog. */
+
+ err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
+ if (!ASSERT_OK(err, "update jmp_table"))
+ goto out;
+
+ freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
+ prog_fd, "subprog_tc");
+ if (!ASSERT_ERR_PTR(freplace_link, "attach_freplace failure"))
+ goto out;
+
+ err = bpf_map_delete_elem(map_fd, &key);
+ if (!ASSERT_OK(err, "delete_elem from jmp_table"))
+ goto out;
+
+ /* Fail to update an extended prog to prog_array map. */
+
+ freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
+ prog_fd, "subprog_tc");
+ if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
+ goto out;
+
+ err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
+ if (!ASSERT_ERR(err, "update jmp_table failure"))
+ goto out;
+
+out:
+ bpf_link__destroy(freplace_link);
+ tailcall_freplace__destroy(freplace_skel);
+ tc_bpf2bpf__destroy(tc_skel);
+}
+
void test_tailcalls(void)
{
if (test__start_subtest("tailcall_1"))
@@ -1606,4 +1699,6 @@ void test_tailcalls(void)
test_tailcall_bpf2bpf_hierarchy_3();
if (test__start_subtest("tailcall_freplace"))
test_tailcall_freplace();
+ if (test__start_subtest("tailcall_bpf2bpf_freplace"))
+ test_tailcall_bpf2bpf_freplace();
}
diff --git a/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c b/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
index 8a0632c37839a..34f3c780194e4 100644
--- a/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
+++ b/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
@@ -4,11 +4,30 @@
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
+struct {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __uint(max_entries, 1);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+} jmp_table SEC(".maps");
+
+__noinline
+int subprog_tailcall_tc(struct __sk_buff *skb)
+{
+ int ret = 1;
+
+ bpf_tail_call_static(skb, &jmp_table, 0);
+ __sink(skb);
+ __sink(ret);
+ return ret;
+}
+
__noinline
-int subprog(struct __sk_buff *skb)
+int subprog_tc(struct __sk_buff *skb)
{
int ret = 1;
+ __sink(skb);
__sink(ret);
return ret;
}
@@ -16,7 +35,8 @@ int subprog(struct __sk_buff *skb)
SEC("tc")
int entry_tc(struct __sk_buff *skb)
{
- return subprog(skb);
+ subprog_tc(skb);
+ return subprog_tailcall_tc(skb);
}
char __license[] SEC("license") = "GPL";
--
2.44.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH bpf-next v4 4/4] selftests/bpf: Add cases to test tailcall in freplace
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
` (2 preceding siblings ...)
2024-09-29 13:27 ` [PATCH bpf-next v4 3/4] selftests/bpf: Add a test case to confirm a tailcall infinite loop issue has been prevented Leon Hwang
@ 2024-09-29 13:27 ` Leon Hwang
2024-10-01 11:14 ` [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Eduard Zingerman
4 siblings, 0 replies; 14+ messages in thread
From: Leon Hwang @ 2024-09-29 13:27 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, leon.hwang, kernel-patches-bot
cd tools/testing/selftests/bpf; ./test_progs -t tailcalls
335/27 tailcalls/tailcall_bpf2bpf_hierarchy_freplace_1:OK
335/28 tailcalls/tailcall_bpf2bpf_hierarchy_freplace_2:OK
335 tailcalls:OK
Summary: 1/28 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/tailcalls.c | 97 +++++++++++++++++++
.../tailcall_bpf2bpf_hierarchy_freplace.c | 30 ++++++
.../testing/selftests/bpf/progs/tc_bpf2bpf.c | 13 +++
3 files changed, 140 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_freplace.c
diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index fa3f3bb11b098..0564ad6c9b288 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -5,6 +5,7 @@
#include "tailcall_poke.skel.h"
#include "tailcall_bpf2bpf_hierarchy2.skel.h"
#include "tailcall_bpf2bpf_hierarchy3.skel.h"
+#include "tailcall_bpf2bpf_hierarchy_freplace.skel.h"
#include "tailcall_freplace.skel.h"
#include "tc_bpf2bpf.skel.h"
@@ -1649,6 +1650,98 @@ static void test_tailcall_bpf2bpf_freplace(void)
tc_bpf2bpf__destroy(tc_skel);
}
+static void test_tailcall_bpf2bpf_hierarchy_freplace(bool freplace_subprog,
+ bool target_entry2,
+ int count1, int count2)
+{
+ struct tailcall_bpf2bpf_hierarchy_freplace *freplace_skel = NULL;
+ struct bpf_link *freplace_link = NULL;
+ struct tc_bpf2bpf *tc_skel = NULL;
+ int prog_fd, map_fd;
+ char buff[128] = {};
+ int err, key, val;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = buff,
+ .data_size_in = sizeof(buff),
+ .repeat = 1,
+ );
+
+ tc_skel = tc_bpf2bpf__open_and_load();
+ if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
+ goto out;
+
+ prog_fd = bpf_program__fd(target_entry2 ? tc_skel->progs.entry_tc_2 :
+ tc_skel->progs.entry_tc);
+ freplace_skel = tailcall_bpf2bpf_hierarchy_freplace__open();
+ if (!ASSERT_OK_PTR(freplace_skel, "tailcall_bpf2bpf_hierarchy_freplace__open"))
+ goto out;
+
+ err = bpf_program__set_attach_target(freplace_skel->progs.entry_freplace,
+ prog_fd, freplace_subprog ?
+ "subprog_tailcall_tc" : "entry_tc");
+ if (!ASSERT_OK(err, "set_attach_target"))
+ goto out;
+
+ err = tailcall_bpf2bpf_hierarchy_freplace__load(freplace_skel);
+ if (!ASSERT_OK(err, "tailcall_bpf2bpf_hierarchy_freplace__load"))
+ goto out;
+
+ val = bpf_program__fd(freplace_skel->progs.entry_freplace);
+ map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
+ key = 0;
+ err = bpf_map_update_elem(map_fd, &key, &val, BPF_ANY);
+ if (!ASSERT_OK(err, "update jmp_table"))
+ goto out;
+
+ freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
+ prog_fd, freplace_subprog ?
+ "subprog_tailcall_tc" : "entry_tc");
+ if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
+ goto out;
+
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_run_opts");
+ ASSERT_EQ(topts.retval, count1, "test_run_opts retval");
+
+ err = bpf_map_delete_elem(map_fd, &key);
+ if (!ASSERT_OK(err, "delete_elem from jmp_table"))
+ goto out;
+
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+ ASSERT_OK(err, "test_run_opts again");
+ ASSERT_EQ(topts.retval, count2, "test_run_opts retval again");
+
+out:
+ bpf_link__destroy(freplace_link);
+ tailcall_bpf2bpf_hierarchy_freplace__destroy(freplace_skel);
+ tc_bpf2bpf__destroy(tc_skel);
+}
+
+/* test_tailcall_bpf2bpf_hierarchy_freplace_1 checks the count value of tail
+ * call limit enforcement matches with expectation for the case:
+ *
+ * subprog_tail --tailcall-> entry_freplace
+ * entry_tc --jump-> entry_freplace <
+ * subprog_tail --tailcall-> entry_freplace
+ */
+static void test_tailcall_bpf2bpf_hierarchy_freplace_1(void)
+{
+ test_tailcall_bpf2bpf_hierarchy_freplace(false, false, 34, 35);
+}
+
+/* test_tailcall_bpf2bpf_hierarchy_freplace_2 checks the count value of tail
+ * call limit enforcement matches with expectation for the case:
+ *
+ * subprog_tail --tailcall-> entry_freplace
+ * entry_tc_2 --> subprog_tailcall_tc (call 10 times) --jump-> entry_freplace <
+ * subprog_tail --tailcall-> entry_freplace
+ */
+static void test_tailcall_bpf2bpf_hierarchy_freplace_2(void)
+{
+ test_tailcall_bpf2bpf_hierarchy_freplace(true, true, 340, 350);
+}
+
void test_tailcalls(void)
{
if (test__start_subtest("tailcall_1"))
@@ -1701,4 +1794,8 @@ void test_tailcalls(void)
test_tailcall_freplace();
if (test__start_subtest("tailcall_bpf2bpf_freplace"))
test_tailcall_bpf2bpf_freplace();
+ if (test__start_subtest("tailcall_bpf2bpf_hierarchy_freplace_1"))
+ test_tailcall_bpf2bpf_hierarchy_freplace_1();
+ if (test__start_subtest("tailcall_bpf2bpf_hierarchy_freplace_2"))
+ test_tailcall_bpf2bpf_hierarchy_freplace_2();
}
diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_freplace.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_freplace.c
new file mode 100644
index 0000000000000..6f7c1fac9ddb7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_freplace.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __uint(max_entries, 1);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+} jmp_table SEC(".maps");
+
+int count = 0;
+
+static __noinline
+int subprog_tail(struct __sk_buff *skb)
+{
+ bpf_tail_call_static(skb, &jmp_table, 0);
+ return 0;
+}
+
+SEC("freplace")
+int entry_freplace(struct __sk_buff *skb)
+{
+ count++;
+ subprog_tail(skb);
+ subprog_tail(skb);
+ return count;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c b/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
index 34f3c780194e4..beacf60a52677 100644
--- a/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
+++ b/tools/testing/selftests/bpf/progs/tc_bpf2bpf.c
@@ -39,4 +39,17 @@ int entry_tc(struct __sk_buff *skb)
return subprog_tailcall_tc(skb);
}
+SEC("tc")
+int entry_tc_2(struct __sk_buff *skb)
+{
+ int ret, i;
+
+ for (i = 0; i < 10; i++) {
+ ret = subprog_tailcall_tc(skb);
+ __sink(ret);
+ }
+
+ return ret;
+}
+
char __license[] SEC("license") = "GPL";
--
2.44.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-09-29 13:27 ` [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog Leon Hwang
@ 2024-09-30 1:53 ` Leon Hwang
2024-10-04 19:33 ` Alexei Starovoitov
0 siblings, 1 reply; 14+ messages in thread
From: Leon Hwang @ 2024-09-30 1:53 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, eddyz87, iii, kernel-patches-bot
On 29/9/24 21:27, Leon Hwang wrote:
> Alongside previous patch, the infinite loop issue caused by combination of
> tailcal and freplace can be prevented completely.
>
> The previous patch can not prevent the use case that updates a prog to
> prog_array map and then extends subprog of the prog with freplace prog.
>
> This patch fixes the case by preventing extending a prog, which has been
> updated to prog_array map, with freplace prog.
>
> If a prog has been updated to prog_array map, it or its subprog can not
> be extended by freplace prog.
>
> Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
> ---
> include/linux/bpf.h | 3 ++-
> kernel/bpf/arraymap.c | 9 ++++++++-
> kernel/bpf/syscall.c | 11 +++++++++++
> 3 files changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index aac6d2f42830c..dc19ad99e2857 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -1484,7 +1484,8 @@ struct bpf_prog_aux {
> bool exception_cb;
> bool exception_boundary;
> bool is_extended; /* true if extended by freplace program */
> - struct mutex ext_mutex; /* mutex for is_extended */
> + u32 prog_array_member_cnt; /* counts how many times as member of prog_array */
> + struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
> struct bpf_arena *arena;
> /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
> const struct btf_type *attach_func_proto;
> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> index 4a4de4f014be9..91b5bdf4dc72d 100644
> --- a/kernel/bpf/arraymap.c
> +++ b/kernel/bpf/arraymap.c
> @@ -957,6 +957,8 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
>
> mutex_lock(&prog->aux->ext_mutex);
> is_extended = prog->aux->is_extended;
> + if (!is_extended)
> + prog->aux->prog_array_member_cnt++;
prog_array_member_cnt must check U32_MAX before incrementing. Or it will
overflow u32. So it will be better like:
mutex_lock(&prog->aux->ext_mutex);
is_invalid = prog->aux->is_extended || prog->aux->prog_array_member_cnt
== U32_MAX;
if (!is_invalid)
prog->aux->prog_array_member_cnt++;
mutex_unlock(&prog->aux->ext_mutex);
if (is_invalid)
goto out_put_prog;
Thanks,
Leon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map
2024-09-29 13:27 ` [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map Leon Hwang
@ 2024-10-01 11:13 ` Eduard Zingerman
2024-10-01 13:20 ` Leon Hwang
0 siblings, 1 reply; 14+ messages in thread
From: Eduard Zingerman @ 2024-10-01 11:13 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, iii, kernel-patches-bot
On Sun, 2024-09-29 at 21:27 +0800, Leon Hwang wrote:
[...]
> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> index 79660e3fca4c1..4a4de4f014be9 100644
> --- a/kernel/bpf/arraymap.c
> +++ b/kernel/bpf/arraymap.c
> @@ -947,16 +947,29 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
> struct file *map_file, int fd)
> {
> struct bpf_prog *prog = bpf_prog_get(fd);
> + bool is_extended;
>
> if (IS_ERR(prog))
> return prog;
>
> - if (!bpf_prog_map_compatible(map, prog)) {
> - bpf_prog_put(prog);
> - return ERR_PTR(-EINVAL);
> - }
> + if (!bpf_prog_map_compatible(map, prog))
> + goto out_put_prog;
> +
> + mutex_lock(&prog->aux->ext_mutex);
> + is_extended = prog->aux->is_extended;
> + mutex_unlock(&prog->aux->ext_mutex);
> + if (is_extended)
> + /* Extended prog can not be tail callee. It's to prevent a
> + * potential infinite loop like:
> + * tail callee prog entry -> tail callee prog subprog ->
> + * freplace prog entry --tailcall-> tail callee prog entry.
> + */
> + goto out_put_prog;
Nit: I think return value should be -EBUSY in this case.
>
> return prog;
> +out_put_prog:
> + bpf_prog_put(prog);
> + return ERR_PTR(-EINVAL);
> }
>
[...]
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index a8f1808a1ca54..db17c52fa35db 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -3212,14 +3212,23 @@ static void bpf_tracing_link_release(struct bpf_link *link)
> {
> struct bpf_tracing_link *tr_link =
> container_of(link, struct bpf_tracing_link, link.link);
> -
> - WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
> - tr_link->trampoline));
> + struct bpf_prog *tgt_prog = tr_link->tgt_prog;
> +
> + if (link->prog->type == BPF_PROG_TYPE_EXT) {
> + mutex_lock(&tgt_prog->aux->ext_mutex);
> + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
> + tr_link->trampoline));
> + tgt_prog->aux->is_extended = false;
In case if unlink fails is_extended should not be reset.
> + mutex_unlock(&tgt_prog->aux->ext_mutex);
> + } else {
> + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
> + tr_link->trampoline));
> + }
>
> bpf_trampoline_put(tr_link->trampoline);
>
> /* tgt_prog is NULL if target is a kernel function */
> - if (tr_link->tgt_prog)
> + if (tgt_prog)
> bpf_prog_put(tr_link->tgt_prog);
> }
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
` (3 preceding siblings ...)
2024-09-29 13:27 ` [PATCH bpf-next v4 4/4] selftests/bpf: Add cases to test tailcall in freplace Leon Hwang
@ 2024-10-01 11:14 ` Eduard Zingerman
4 siblings, 0 replies; 14+ messages in thread
From: Eduard Zingerman @ 2024-10-01 11:14 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, iii, kernel-patches-bot
On Sun, 2024-09-29 at 21:27 +0800, Leon Hwang wrote:
> Previously, I fixed a tailcall infinite loop issue caused by trampoline[0].
>
> At this time, I fix a tailcall infinite loop issue caused by tailcall and
> freplace combination by preventing updating extended prog to prog_array map
> and preventing extending tail callee prog with freplace:
>
> 1. If a prog or its subprog has been extended by freplace prog, the prog
> can not be updated to prog_array map.
> 2. If a prog has been updated to prog_array map, it or its subprog can not
> be extended by freplace prog.
So, once this series is applied we essentially have:
- three variables:
- tgt_prog->aux->is_extended
- tgt_prog->aux->prog_array_member_cnt
- trampoline->extension_prog
- four operations:
- link/attach extension program 'prog' using trampoline 'tr'
- unlink/detach extension program 'prog' using trampoline 'tr'
- put program 'tgt_prog' into prog array
- remove program 'tgt_prog' from prog array
And above four operations have the following pseudo-code with regards
to update of the variables:
- link/attach extension program 'prog' using trampoline 'tr':
with lock(tgt_prog->ext_mutex):
if tgt_prog->aux->prog_array_member_cnt:
return error
if tr->extension_prog:
return error
tr->extension_prog = prog
tgt_prog->is_extended = true
- unlink/detach extension program 'prog' using trampoline 'tr':
with lock(tgt_prog->ext_mutex):
tr->extension_prog = NULL
tgt_prog->is_extended = false
- put program 'tgt_prog' into prog array:
with lock(tgt_prog->ext_mutex):
if tgt_prog->aux->is_extended:
return error
tgt_prog->aux->prog_array_member_cnt++
- remove program 'tgt_prog' from prog array:
with lock(tgt_prog->ext_mutex):
tgt_prog->aux->prog_array_member_cnt--
I think this is correct, would be great if someone with more
concurrency related experience would take a look.
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map
2024-10-01 11:13 ` Eduard Zingerman
@ 2024-10-01 13:20 ` Leon Hwang
2024-10-01 16:54 ` Eduard Zingerman
0 siblings, 1 reply; 14+ messages in thread
From: Leon Hwang @ 2024-10-01 13:20 UTC (permalink / raw)
To: Eduard Zingerman, bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, iii, kernel-patches-bot
On 2024/10/1 19:13, Eduard Zingerman wrote:
> On Sun, 2024-09-29 at 21:27 +0800, Leon Hwang wrote:
>
> [...]
>
>> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
>> index 79660e3fca4c1..4a4de4f014be9 100644
>> --- a/kernel/bpf/arraymap.c
>> +++ b/kernel/bpf/arraymap.c
>> @@ -947,16 +947,29 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
>> struct file *map_file, int fd)
>> {
>> struct bpf_prog *prog = bpf_prog_get(fd);
>> + bool is_extended;
>>
>> if (IS_ERR(prog))
>> return prog;
>>
>> - if (!bpf_prog_map_compatible(map, prog)) {
>> - bpf_prog_put(prog);
>> - return ERR_PTR(-EINVAL);
>> - }
>> + if (!bpf_prog_map_compatible(map, prog))
>> + goto out_put_prog;
>> +
>> + mutex_lock(&prog->aux->ext_mutex);
>> + is_extended = prog->aux->is_extended;
>> + mutex_unlock(&prog->aux->ext_mutex);
>> + if (is_extended)
>> + /* Extended prog can not be tail callee. It's to prevent a
>> + * potential infinite loop like:
>> + * tail callee prog entry -> tail callee prog subprog ->
>> + * freplace prog entry --tailcall-> tail callee prog entry.
>> + */
>> + goto out_put_prog;
>
> Nit: I think return value should be -EBUSY in this case.
Ack.
>
>>
>> return prog;
>> +out_put_prog:
>> + bpf_prog_put(prog);
>> + return ERR_PTR(-EINVAL);
>> }
>>
>
> [...]
>
>> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
>> index a8f1808a1ca54..db17c52fa35db 100644
>> --- a/kernel/bpf/syscall.c
>> +++ b/kernel/bpf/syscall.c
>> @@ -3212,14 +3212,23 @@ static void bpf_tracing_link_release(struct bpf_link *link)
>> {
>> struct bpf_tracing_link *tr_link =
>> container_of(link, struct bpf_tracing_link, link.link);
>> -
>> - WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
>> - tr_link->trampoline));
>> + struct bpf_prog *tgt_prog = tr_link->tgt_prog;
>> +
>> + if (link->prog->type == BPF_PROG_TYPE_EXT) {
>> + mutex_lock(&tgt_prog->aux->ext_mutex);
>> + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
>> + tr_link->trampoline));
>> + tgt_prog->aux->is_extended = false;
>
> In case if unlink fails is_extended should not be reset.
>
Nope.
In bpf_trampoline_unlink_prog(), 'tr->extension_prog = NULL;' always no
matter whether fail to unlink.
So, it should reset is_extended always too.
Thanks,
Leon
>> + mutex_unlock(&tgt_prog->aux->ext_mutex);
>> + } else {
>> + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
>> + tr_link->trampoline));
>> + }
>>
>> bpf_trampoline_put(tr_link->trampoline);
>>
>> /* tgt_prog is NULL if target is a kernel function */
>> - if (tr_link->tgt_prog)
>> + if (tgt_prog)
>> bpf_prog_put(tr_link->tgt_prog);
>> }
>
> [...]
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map
2024-10-01 13:20 ` Leon Hwang
@ 2024-10-01 16:54 ` Eduard Zingerman
0 siblings, 0 replies; 14+ messages in thread
From: Eduard Zingerman @ 2024-10-01 16:54 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: ast, daniel, andrii, toke, martin.lau, yonghong.song, puranjay,
xukuohai, iii, kernel-patches-bot
On Tue, 2024-10-01 at 21:20 +0800, Leon Hwang wrote:
[...]
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index a8f1808a1ca54..db17c52fa35db 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -3212,14 +3212,23 @@ static void bpf_tracing_link_release(struct bpf_link *link)
> > > {
> > > struct bpf_tracing_link *tr_link =
> > > container_of(link, struct bpf_tracing_link, link.link);
> > > -
> > > - WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
> > > - tr_link->trampoline));
> > > + struct bpf_prog *tgt_prog = tr_link->tgt_prog;
> > > +
> > > + if (link->prog->type == BPF_PROG_TYPE_EXT) {
> > > + mutex_lock(&tgt_prog->aux->ext_mutex);
> > > + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
> > > + tr_link->trampoline));
> > > + tgt_prog->aux->is_extended = false;
> >
> > In case if unlink fails is_extended should not be reset.
> >
>
> Nope.
>
> In bpf_trampoline_unlink_prog(), 'tr->extension_prog = NULL;' always no
> matter whether fail to unlink.
>
> So, it should reset is_extended always too.
Hm, you are correct, sorry for the noise.
It is unfortunate that these updates are separated in the code, tbh.
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-09-30 1:53 ` Leon Hwang
@ 2024-10-04 19:33 ` Alexei Starovoitov
2024-10-04 20:37 ` Eduard Zingerman
2024-10-04 20:52 ` Eduard Zingerman
0 siblings, 2 replies; 14+ messages in thread
From: Alexei Starovoitov @ 2024-10-04 19:33 UTC (permalink / raw)
To: Leon Hwang
Cc: Leon Hwang, bpf, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Toke Høiland-Jørgensen,
Martin KaFai Lau, Yonghong Song, Puranjay Mohan, Xu Kuohai,
Eddy Z, Ilya Leoshkevich, kernel-patches-bot
On Sun, Sep 29, 2024 at 6:54 PM Leon Hwang <hffilwlqm@gmail.com> wrote:
>
>
>
> On 29/9/24 21:27, Leon Hwang wrote:
> > Alongside previous patch, the infinite loop issue caused by combination of
> > tailcal and freplace can be prevented completely.
> >
> > The previous patch can not prevent the use case that updates a prog to
> > prog_array map and then extends subprog of the prog with freplace prog.
> >
> > This patch fixes the case by preventing extending a prog, which has been
> > updated to prog_array map, with freplace prog.
> >
> > If a prog has been updated to prog_array map, it or its subprog can not
> > be extended by freplace prog.
> >
> > Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
> > ---
> > include/linux/bpf.h | 3 ++-
> > kernel/bpf/arraymap.c | 9 ++++++++-
> > kernel/bpf/syscall.c | 11 +++++++++++
> > 3 files changed, 21 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index aac6d2f42830c..dc19ad99e2857 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -1484,7 +1484,8 @@ struct bpf_prog_aux {
> > bool exception_cb;
> > bool exception_boundary;
> > bool is_extended; /* true if extended by freplace program */
> > - struct mutex ext_mutex; /* mutex for is_extended */
> > + u32 prog_array_member_cnt; /* counts how many times as member of prog_array */
> > + struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
> > struct bpf_arena *arena;
> > /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
> > const struct btf_type *attach_func_proto;
> > diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> > index 4a4de4f014be9..91b5bdf4dc72d 100644
> > --- a/kernel/bpf/arraymap.c
> > +++ b/kernel/bpf/arraymap.c
> > @@ -957,6 +957,8 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
> >
> > mutex_lock(&prog->aux->ext_mutex);
> > is_extended = prog->aux->is_extended;
> > + if (!is_extended)
> > + prog->aux->prog_array_member_cnt++;
>
> prog_array_member_cnt must check U32_MAX before incrementing. Or it will
> overflow u32. So it will be better like:
>
> mutex_lock(&prog->aux->ext_mutex);
> is_invalid = prog->aux->is_extended || prog->aux->prog_array_member_cnt
> == U32_MAX;
No. Just make it u64 instead.
btw the whole thing can be done with a single atomic64_t:
- set it to 1 at the start then
- prog_fd_array_get_ptr() will do
atomic64_inc_not_zero
- prog_fd_array_put_ptr() will do
atomic64_add_unless(,-1, 1)
- freplace attach will do
cmpxchg(,1,0)
so 1 - initial state
2,3,.. - prog in prog_array
0 - prog was extended.
If == 0 -> cannot add to prog_array
if > 1 -> cannot freplace.
but it's too clever.
It's better to use mutex and keep bool + count,
but extra mutex is unnecessary.
Reuse prog->aux->dst_mutex.
Grab it prog_fd_array_get_ptr() and do the check and cnt++
Also pls combine patch 1 and 2.
They do one logical step.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-10-04 19:33 ` Alexei Starovoitov
@ 2024-10-04 20:37 ` Eduard Zingerman
2024-10-04 20:52 ` Eduard Zingerman
1 sibling, 0 replies; 14+ messages in thread
From: Eduard Zingerman @ 2024-10-04 20:37 UTC (permalink / raw)
To: Alexei Starovoitov, Leon Hwang
Cc: Leon Hwang, bpf, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Toke Høiland-Jørgensen,
Martin KaFai Lau, Yonghong Song, Puranjay Mohan, Xu Kuohai,
Ilya Leoshkevich, kernel-patches-bot
On Fri, 2024-10-04 at 12:33 -0700, Alexei Starovoitov wrote:
[...]
> so 1 - initial state
> 2,3,.. - prog in prog_array
> 0 - prog was extended.
This sounds interesting, but need to think a bit.
> If == 0 -> cannot add to prog_array
> if > 1 -> cannot freplace.
>
> but it's too clever.
> It's better to use mutex and keep bool + count,
> but extra mutex is unnecessary.
> Reuse prog->aux->dst_mutex.
> Grab it prog_fd_array_get_ptr() and do the check and cnt++
I think it is not possible to grab the correct mutex in
prog_fd_array_get_ptr().
bpf_tracing_prog_attach() operates on two programs:
- one named 'prog' is the freplace program;
- another named 'tgt_prog' is the program to attach 'prog' to.
bpf_tracing_prog_attach() grabs prog->aux->dst_mutex.
Inside prog_fd_array_get_ptr() there is only a pointer to program
being put into array, potential target of the freplace.
From bpf_tracing_prog_attach() it is referred as 'tgt_prog'.
As far as I understand, there is no way to get a pointer to an active
freplace program from prog_fd_array_get_ptr().
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-10-04 19:33 ` Alexei Starovoitov
2024-10-04 20:37 ` Eduard Zingerman
@ 2024-10-04 20:52 ` Eduard Zingerman
2024-10-04 23:30 ` Alexei Starovoitov
1 sibling, 1 reply; 14+ messages in thread
From: Eduard Zingerman @ 2024-10-04 20:52 UTC (permalink / raw)
To: Alexei Starovoitov, Leon Hwang
Cc: Leon Hwang, bpf, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Toke Høiland-Jørgensen,
Martin KaFai Lau, Yonghong Song, Puranjay Mohan, Xu Kuohai,
Ilya Leoshkevich, kernel-patches-bot
On Fri, 2024-10-04 at 12:33 -0700, Alexei Starovoitov wrote:
[...]
> btw the whole thing can be done with a single atomic64_t:
> - set it to 1 at the start then
>
> - prog_fd_array_get_ptr() will do
> atomic64_inc_not_zero
>
> - prog_fd_array_put_ptr() will do
> atomic64_add_unless(,-1, 1)
>
> - freplace attach will do
> cmpxchg(,1,0)
>
> so 1 - initial state
> 2,3,.. - prog in prog_array
> 0 - prog was extended.
>
> If == 0 -> cannot add to prog_array
> if > 1 -> cannot freplace.
I think this should work, because we no longer need to jungle two values.
I kinda like it.
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog
2024-10-04 20:52 ` Eduard Zingerman
@ 2024-10-04 23:30 ` Alexei Starovoitov
0 siblings, 0 replies; 14+ messages in thread
From: Alexei Starovoitov @ 2024-10-04 23:30 UTC (permalink / raw)
To: Eduard Zingerman
Cc: Leon Hwang, Leon Hwang, bpf, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Toke Høiland-Jørgensen,
Martin KaFai Lau, Yonghong Song, Puranjay Mohan, Xu Kuohai,
Ilya Leoshkevich, kernel-patches-bot
On Fri, Oct 4, 2024 at 1:52 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Fri, 2024-10-04 at 12:33 -0700, Alexei Starovoitov wrote:
>
> [...]
>
> > btw the whole thing can be done with a single atomic64_t:
> > - set it to 1 at the start then
> >
> > - prog_fd_array_get_ptr() will do
> > atomic64_inc_not_zero
> >
> > - prog_fd_array_put_ptr() will do
> > atomic64_add_unless(,-1, 1)
> >
> > - freplace attach will do
> > cmpxchg(,1,0)
> >
> > so 1 - initial state
> > 2,3,.. - prog in prog_array
> > 0 - prog was extended.
> >
> > If == 0 -> cannot add to prog_array
> > if > 1 -> cannot freplace.
>
> I think this should work, because we no longer need to jungle two values.
> I kinda like it.
It's a bit too clever.
With mutex it's much easier to reason about:
struct bpf_prog_aux {
mutex ext_mutex;
bool is_extended;
u64 prog_array_member_cnt;
};
freplace link on tgt_prog:
guard(mutex)(&aux->ext_mutex);
if (aux->prog_array_member_cnt) {
// reject freplace
} else {
aux->is_extended = true;
}
freplace unlink:
guard(mutex)(&aux->ext_mutex);
aux->is_extended = false;
and similar in prog_fd_array_get_ptr():
guard(mutex)(&aux->ext_mutex);
if (aux->is_extended) {
// reject adding to prog_array
} else {
aux->prog_array_member_cnt++;
}
in prog_fd_array_put_ptr():
guard(mutex)(&aux->ext_mutex);
aux->prog_array_member_cnt--;
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2024-10-04 23:30 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-29 13:27 [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 1/4] bpf: Prevent updating extended prog to prog_array map Leon Hwang
2024-10-01 11:13 ` Eduard Zingerman
2024-10-01 13:20 ` Leon Hwang
2024-10-01 16:54 ` Eduard Zingerman
2024-09-29 13:27 ` [PATCH bpf-next v4 2/4] bpf: Prevent extending tail callee prog with freplace prog Leon Hwang
2024-09-30 1:53 ` Leon Hwang
2024-10-04 19:33 ` Alexei Starovoitov
2024-10-04 20:37 ` Eduard Zingerman
2024-10-04 20:52 ` Eduard Zingerman
2024-10-04 23:30 ` Alexei Starovoitov
2024-09-29 13:27 ` [PATCH bpf-next v4 3/4] selftests/bpf: Add a test case to confirm a tailcall infinite loop issue has been prevented Leon Hwang
2024-09-29 13:27 ` [PATCH bpf-next v4 4/4] selftests/bpf: Add cases to test tailcall in freplace Leon Hwang
2024-10-01 11:14 ` [PATCH bpf-next v4 0/4] bpf: Fix tailcall infinite loop caused by freplace Eduard Zingerman
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.