Merge branch 'bpf-fix-warning-in-bpf_tracing_link_release'

Leon Hwang says:

====================
bpf: Fix WARNING in bpf_tracing_link_release

The trampoline could be corrupted by the blindly
'tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX' in verifier.

1. A fexit attached to a tail_call_reachable prog. 'tr->flags' became
   'BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_TAIL_CALL_CTX'. And, the
   trampoline would poke the target prog's nop insn using jmp insn instead
   of call insn.
2. Another fexit loaded with the same tail_call_reachable prog target.
   'tr->flags' became 'BPF_TRAMP_F_TAIL_CALL_CTX'.
3. Close the first fexit link. Due to no BPF_TRAMP_F_CALL_ORIG in
   'tr->flags', the trampoline will fail to restore the prog's nop insn
   using call insn.

[    3.410719] WARNING: kernel/bpf/syscall.c:3551 at bpf_tracing_link_release+0x53/0x60, CPU#1: test_progs/98
...
[    3.428793]  bpf_link_free+0x58/0x130
[    3.429293]  bpf_link_release+0x23/0x30

Fix the warning by updating 'tr->flags' with '|=' and lock.

Changes:
v1 -> v2:
* Update the patch #1 message with 'tr->flags' change. (per Jiri)
* Drop the 'link' and the last 'if' in patch #2. (per Jiri)
* v1: https://lore.kernel.org/bpf/20260721133036.49265-1-leon.hwang@linux.dev/
====================

Link: https://patch.msgid.link/20260722151909.69142-1-leon.hwang@linux.dev
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi
2026-07-24 22:34:46 +02:00
4 changed files with 58 additions and 1 deletions

View File

@@ -1523,6 +1523,7 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
struct bpf_tracing_multi_link *link);
int bpf_trampoline_multi_detach(struct bpf_prog *prog,
struct bpf_tracing_multi_link *link);
void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags);
/*
* When the architecture supports STATIC_CALL replace the bpf_dispatcher_fn
@@ -1646,6 +1647,7 @@ static inline int bpf_trampoline_multi_detach(struct bpf_prog *prog,
{
return -ENOTSUPP;
}
static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags) {}
#endif
struct bpf_func_info_aux {

View File

@@ -670,6 +670,13 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
return ERR_PTR(err);
}
void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags)
{
trampoline_lock(tr);
tr->flags |= flags;
trampoline_unlock(tr);
}
static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex,
const struct bpf_trampoline_ops *ops, void *data)
{

View File

@@ -19523,7 +19523,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
return -ENOMEM;
if (tgt_prog && tgt_prog->aux->tail_call_reachable)
tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX;
bpf_trampoline_set_flags(tr, BPF_TRAMP_F_TAIL_CALL_CTX);
prog->aux->dst_trampoline = tr;
return 0;

View File

@@ -13,6 +13,8 @@
#include "tailcall_cgrp_storage.skel.h"
#include "tailcall_sleepable.skel.h"
#include "tailcall_callback.skel.h"
#include "tailcall_bpf2bpf2.skel.h"
#include "tailcall_bpf2bpf_fexit.skel.h"
/* test_tailcall_1 checks basic functionality by patching multiple locations
* in a single program for a single tail call slot with nop->jmp, jmp->nop
@@ -1907,6 +1909,50 @@ static void test_tailcall_callback(void)
RUN_TESTS(tailcall_callback);
}
static void test_tailcall_bpf2bpf_fexit_links(void)
{
struct tailcall_bpf2bpf_fexit *skel1 = NULL, *skel2 = NULL;
struct tailcall_bpf2bpf2 *skel_tc;
int err, prog_fd;
skel_tc = tailcall_bpf2bpf2__open_and_load();
if (!ASSERT_OK_PTR(skel_tc, "tailcall_bpf2bpf2__open_and_load"))
return;
skel1 = tailcall_bpf2bpf_fexit__open();
if (!ASSERT_OK_PTR(skel1, "tailcall_bpf2bpf_fexit__open"))
goto out;
prog_fd = bpf_program__fd(skel_tc->progs.classifier_0);
err = bpf_program__set_attach_target(skel1->progs.fexit, prog_fd, "subprog_tail");
if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
goto out;
err = tailcall_bpf2bpf_fexit__load(skel1);
if (!ASSERT_OK(err, "tailcall_bpf2bpf_fexit__load"))
goto out;
skel1->links.fexit = bpf_program__attach_trace(skel1->progs.fexit);
if (!ASSERT_OK_PTR(skel1->links.fexit, "bpf_program__attach_trace"))
goto out;
skel2 = tailcall_bpf2bpf_fexit__open();
if (!ASSERT_OK_PTR(skel2, "tailcall_bpf2bpf_fexit__open"))
goto out;
err = bpf_program__set_attach_target(skel2->progs.fexit, prog_fd, "subprog_tail");
if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
goto out;
err = tailcall_bpf2bpf_fexit__load(skel2);
ASSERT_OK(err, "tailcall_bpf2bpf_fexit__load");
out:
tailcall_bpf2bpf_fexit__destroy(skel1);
tailcall_bpf2bpf_fexit__destroy(skel2);
tailcall_bpf2bpf2__destroy(skel_tc);
}
void test_tailcalls(void)
{
if (test__start_subtest("tailcall_1"))
@@ -1974,4 +2020,6 @@ void test_tailcalls(void)
if (test__start_subtest("tailcall_cgrp_storage_no_storage_bridge"))
test_tailcall_cgrp_storage_no_storage_bridge();
test_tailcall_callback();
if (test__start_subtest("tailcall_bpf2bpf_fexit_links"))
test_tailcall_bpf2bpf_fexit_links();
}