bpf: Fix WARNING in bpf_tracing_link_release
Subsystems (4)
v2 cover 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/[email protected]/
Leon Hwang (2):
bpf: Fix WARNING in bpf_tracing_link_release selftests/bpf: Verify no warning when close fexit link
include/linux/bpf.h | 2 + kernel/bpf/trampoline.c | 7 +++ kernel/bpf/verifier.c | 2 +- .../selftests/bpf/prog_tests/tailcalls.c | 48 +++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-)
--
2.55.0
PATCH v2 1/2 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.
--- include/linux/bpf.h | 2 ++ kernel/bpf/trampoline.c | 7 +++++++ kernel/bpf/verifier.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-)
include/linux/bpf.h +2 -0
@@ -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 {
kernel/bpf/trampoline.c +7 -0
@@ -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) {
kernel/bpf/verifier.c +1 -1
@@ -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 ;
Signature · 1 line
2.55.0
Quoted 72 lines · Leon Hwang
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_reachableprog. '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 sametail_call_reachableprog target.'tr->flags' became 'BPF_TRAMP_F_TAIL_CALL_CTX'.3. Close the first fexit link. Due to no
BPF_TRAMP_F_CALL_ORIGin '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/0x30Fix the warning by updating '
tr->flags' with '|=' and lock.Fixes 2b5dcb31a19a ("bpf, x64: Fix tailcall infinite loop")Acked-by Jiri Olsa <[email protected]>Signed-off-by Leon Hwang <[email protected]>--- include/linux/bpf.h | 2 ++ kernel/bpf/trampoline.c | 7 +++++++ kernel/bpf/verifier.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-)include/linux/bpf.h +2
-0@@ -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 {kernel/bpf/trampoline.c +7
-0@@ -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) {kernel/bpf/verifier.c +0
-0
kernel/bpf/verifier.c static int check_attach_btf_id(struct bpf_verifier_env *env) · line 19529
Quoted 8 lines
kernel/bpf/verifier.c +1
-1@@ -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 ;
PATCH v2 2/2 selftests/bpf: Verify no warning when close fexit link
In reply to Leon HwangAdd a test to verify that there's no WARNING when detaching fexit link by following the repro steps of previous commit.
Without the fix, the WARNING could be triggered by this test.
--- .../selftests/bpf/prog_tests/tailcalls.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+)
tools/testing/selftests/bpf/prog_tests/tailcalls.c +48 -0
@@ -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 (); }
Signature · 1 line
2.55.0
Re: [PATCH bpf-next v2 2/2] selftests/bpf: Verify no warning when close fexit link
Quoted 76 lines · Leon Hwang
Add a test to verify that there's no WARNING when detaching fexit link by following the repro steps of previous commit.
Without the fix, the WARNING could be triggered by this test.
Acked-by Jiri Olsa <[email protected]>Signed-off-by Leon Hwang <[email protected]>--- .../selftests/bpf/prog_tests/tailcalls.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+)tools/testing/selftests/bpf/prog_tests/tailcalls.c +46
-0@@ -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" ))
tools/testing/selftests/bpf/prog_tests/tailcalls.c void test_tailcalls(void) · line 2025
Quoted 6 lines
tools/testing/selftests/bpf/prog_tests/tailcalls.c +2
-0@@ -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 (); }
This series was applied to bpf/bpf-next.git (master) by Kumar Kartikeya Dwivedi <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Kumar Kartikeya Dwivedi <[email protected]>:
Quoted 13 lines · On Wed, 22 Jul 2026 23:19:07 +0800 you wrote:
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_reachableprog. '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 sametail_call_reachableprog target. 'tr->flags' became 'BPF_TRAMP_F_TAIL_CALL_CTX'.
3. Close the first fexit link. Due to noBPF_TRAMP_F_CALL_ORIGin 'tr->flags', the trampoline will fail to restore the prog's nop insn using call insn.
Quoted 1 line
[...]
Here is the summary with links:
- [bpf-next,v2,1/2] bpf: Fix WARNING in bpf_tracing_link_release
https://git.kernel.org/bpf/bpf-next/c/61aaa8782bec - [bpf-next,v2,2/2] selftests/bpf: Verify no warning when close fexit link https://git.kernel.org/bpf/bpf-next/c/09e074666b7d
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html