From nobody Thu Sep 24 13:41:55 2026 Received: from m16.mail.163.com (m16.mail.163.com [220.197.31.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4DD147D925; Wed, 23 Sep 2026 09:07:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.2 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790154462; cv=none; b=GfjxGW5CA2vaFIEMzFO352+rUaD8RbdZ/F6DDc8h3IFG3gXKmDYIAXYghyYdWDCgk4pzxMLVFbPXqp4VzqxnuAxmhpvj9cC23Fnyj/w4cjW5spHam/4Qw54UKb61aYs191JjOPzse3fqkr9desDWIqSA0CP5xXW7wh/5ERNquSY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790154462; c=relaxed/simple; bh=hMeF6zvrbmtd5L0Fl4kFRnK2npu6Nm4haLoxXNRAyRs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZZOz/z/kaYgltrMSIIk1j3JhMLzHk7VWEerv+U9l3/yxvrK1XikWhIGnfQZWeUBUT58M4BZRhDDiY9h68RQ8GW3xiovSwv6wGDi2ZxiWj4WiRlal9/Z/zvdqZFEypITgnQtWToHiuSco8xqacqyL1TI+Ikds/IS8rfHUbBrQNxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=Mj9HmEPg; arc=none smtp.client-ip=220.197.31.2 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="Mj9HmEPg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=/g TM73/F9WSA8hdHaJIMje7bLTqx7CUbbRHTg5nU5F8=; b=Mj9HmEPgqJQd/2cuBw yMb2vmv7/2cHF92s2fN+FAOrEohlZ1u1Qs6Fqk2vUc1CinBHpcwPes8wziMiF4EL ZanFHh9ZJkm5aoKnOkJppBbS/9ykeXxHyYEY0LkZNpaKptXg4Q9oWaug4c8IZOk1 AnLRUy3CpDq8pzpjfOxbJHR0Q= Received: from nec8-i7 (unknown []) by gzsmtp3 (Coremail) with SMTP id PigvCgDXvIWmlrNqQZffAw--.40141S3; Wed, 23 Sep 2026 17:06:49 +0800 (CST) From: chenyuan_fl@163.com To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Alexei Starovoitov , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Eduard Zingerman , Kumar Kartikeya Dwivedi , Martin KaFai Lau , Song Liu , Yonghong Song , Jiri Olsa , Ihor Solodrai , Yuan Chen Subject: [PATCH bpf-next 1/2] bpf: Keep target extended until its last freplace link detaches Date: Wed, 23 Sep 2026 17:06:34 +0800 Message-ID: <20260923090635.368488-2-chenyuan_fl@163.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260923090635.368488-1-chenyuan_fl@163.com> References: <20260923090635.368488-1-chenyuan_fl@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: PigvCgDXvIWmlrNqQZffAw--.40141S3 X-Coremail-Antispam: 1Uf129KBjvJXoWxAFy5Wryxtr4fWF4rtFyrZwb_yoWrCFWUpF WUJr1UCr4UXw4qq34kAw1UZw4a9w1kX3yxGFWrG3W0vFZIqrWkua4UW3sIkFyFvFW0yF1S va10grWDAw1xGFDanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07joNtxUUUUU= X-CM-SenderInfo: xfkh05pxdqswro6rljoofrz/xtbDAQlXFWqzlqk3CwAA3Q Content-Type: text/plain; charset="utf-8" From: Yuan Chen The is_extended / prog_array_member_cnt protocol introduced by commit d6083f040d5d ("bpf: Prevent tailcall infinite loop caused by freplace") keeps a prog extended by a freplace program out of prog_array maps, and vice versa: once a tail call re-enters an extended subprogram, its tail_call_cnt resets on every execution and the loop never terminates. But is_extended is a plain boolean, while one target prog can carry several freplace links at the same time, one on its entry and one on a global subprogram. __bpf_trampoline_unlink_prog() cleared is_extended whenever *any* freplace link detached, so detaching one of two links re-armed the unbounded loop through the remaining one. Replace the is_extended boolean with a count of the freplace links attached to each target prog, so the target stays extended until its last link detaches. Also rename bpf_freplace_check_tgt_prog() to bpf_freplace_link_tgt_prog(), as the helper has never been a pure check: it reserves the target prog on success. Fixes: d6083f040d5d ("bpf: Prevent tailcall infinite loop caused by freplac= e") Signed-off-by: Yuan Chen --- include/linux/bpf.h | 4 ++-- kernel/bpf/arraymap.c | 2 +- kernel/bpf/trampoline.c | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 73bacfc6444d..fc9011b8446d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1773,7 +1773,6 @@ struct bpf_prog_aux { bool xdp_has_frags; bool exception_cb; bool exception_boundary; - bool is_extended; /* true if extended by freplace program */ bool jits_use_priv_stack; bool priv_stack_requested; bool changes_pkt_data; @@ -1785,7 +1784,8 @@ struct bpf_prog_aux { u8 verdict; } sig; u64 prog_array_member_cnt; /* counts how many times as member of prog_arr= ay */ - struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cn= t */ + u64 freplace_link_cnt; /* counts freplace links extending this prog */ + struct mutex ext_mutex; /* mutex for freplace_link_cnt and prog_array_mem= ber_cnt */ struct bpf_arena *arena; void (*recursion_detected)(struct bpf_prog *prog); /* callback if recursi= on is detected */ /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 248b4818178c..3bcff6a0430d 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -974,7 +974,7 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map, } =20 mutex_lock(&prog->aux->ext_mutex); - is_extended =3D prog->aux->is_extended; + is_extended =3D prog->aux->freplace_link_cnt > 0; if (!is_extended) prog->aux->prog_array_member_cnt++; mutex_unlock(&prog->aux->ext_mutex); diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index ed7999ad6c66..d18a913bc80f 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -813,7 +813,7 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tram= p(struct bpf_prog *prog) } } =20 -static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog) +static int bpf_freplace_link_tgt_prog(struct bpf_prog *tgt_prog) { struct bpf_prog_aux *aux =3D tgt_prog->aux; =20 @@ -827,7 +827,7 @@ static int bpf_freplace_check_tgt_prog(struct bpf_prog = *tgt_prog) */ return -EBUSY; =20 - aux->is_extended =3D true; + aux->freplace_link_cnt++; return 0; } =20 @@ -933,7 +933,7 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_= node *node, /* Cannot attach extension if fentry/fexit are in use. */ if (cnt) return -EBUSY; - err =3D bpf_freplace_check_tgt_prog(tgt_prog); + err =3D bpf_freplace_link_tgt_prog(tgt_prog); if (err) return err; tr->extension_prog =3D node->link->prog; @@ -979,7 +979,9 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tram= p_node *node, tr->extension_prog->bpf_func, NULL); tr->extension_prog =3D NULL; guard(mutex)(&tgt_prog->aux->ext_mutex); - tgt_prog->aux->is_extended =3D false; + if (WARN_ON_ONCE(!tgt_prog->aux->freplace_link_cnt)) + return err; + tgt_prog->aux->freplace_link_cnt--; return err; } bpf_trampoline_remove_prog(tr, node); --=20 2.54.0 From nobody Thu Sep 24 13:41:55 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C82E1471274; Wed, 23 Sep 2026 09:07:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790154455; cv=none; b=bkVD4RnKn8EjHlOOUfz8Gd+LByL6zRpASg4z9S2nxtgoSvGJOfyBWY40YC0fTA7e8q6w9s59ioCQOPZHPPtDtHTy7wZ2NhVv/KuWpdV+TI3LPXSe5i88PDbwcCYoFjvrh/MYajTgrHYoDCwrLQOzSggkBa6VGeFdQ9Mi7Rp0YiQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790154455; c=relaxed/simple; bh=4rjEPn5rsRxjqIv65BtjQP5DFSL+taxZZqqx/X+y3WE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k1/LAEfAiSJ20HQmAKK4qJbReZfywY2vAapmn+5fLu90KaWJei4GszxzKdy/pMNzuIWA5hlkrLgfSqbmC/W2eZRgsTA2aRYOQ+5g9w2m3B93U03WOUkKO/PBG4Jzc/YYYghNsOTQ9qLeewQnQ/8Hoo7ACls8enX4Il+Kd5B8KI4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=bRItVp/r; arc=none smtp.client-ip=117.135.210.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="bRItVp/r" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=bc NqT1UVY8xeWIGgXMpMHXdOczJ57YxKCerzPo7aX8A=; b=bRItVp/rD4EC6ndInq QtzHqIIpZMSIyW5pFBz4FvaVSREXaM3be2tV8XS3IE7kTuE96alrXrzTKokdQay3 xLhbj9Lf/bICnOo6evd8V7/pFbQqaMvcpxBlx5tu4/3/cD7wK/04osV5nVBMT8+U H380AILXGAs8K6v1MdjW92EOk= Received: from nec8-i7 (unknown []) by gzsmtp3 (Coremail) with SMTP id PigvCgDXvIWmlrNqQZffAw--.40141S4; Wed, 23 Sep 2026 17:06:51 +0800 (CST) From: chenyuan_fl@163.com To: bpf@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Alexei Starovoitov , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Eduard Zingerman , Kumar Kartikeya Dwivedi , Martin KaFai Lau , Song Liu , Yonghong Song , Jiri Olsa , Ihor Solodrai , Yuan Chen Subject: [PATCH bpf-next 2/2] selftests/bpf: Verify is_extended with multiple freplace links Date: Wed, 23 Sep 2026 17:06:35 +0800 Message-ID: <20260923090635.368488-3-chenyuan_fl@163.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260923090635.368488-1-chenyuan_fl@163.com> References: <20260923090635.368488-1-chenyuan_fl@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: PigvCgDXvIWmlrNqQZffAw--.40141S4 X-Coremail-Antispam: 1Uf129KBjvJXoW3Gr1UKr17Cr45KrWxWFy5CFg_yoW7AFyDpa yjqw1rKr1rXw1qqF47Cw4xW3ya9a1kXry5GryrWwn5Ar1jvr929F18KryDCF9xXrW5t34r Zr1qvrn5G3yxZaUanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07j45lbUUUUU= X-CM-SenderInfo: xfkh05pxdqswro6rljoofrz/xtbDAQtXFWqzlqs3QQAA3a Content-Type: text/plain; charset="utf-8" From: Yuan Chen Extend tc_bpf2bpf with two freplace links, one on entry_tc and one on subprog_tc, and detach the one on the entry: while subprog_tc is still extended, updating entry_tc into a prog_array map must keep failing with -EBUSY, and succeed again once the last link detaches. The test asserts the rejection instead of running the prog, as the update succeeds and the prog loops unbounded on an unfixed kernel. Signed-off-by: Yuan Chen --- .../selftests/bpf/prog_tests/tailcalls.c | 74 +++++++++++++++++++ .../bpf/progs/tailcall_freplace_multi.c | 27 +++++++ 2 files changed, 101 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/tailcall_freplace_mul= ti.c diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/tes= ting/selftests/bpf/prog_tests/tailcalls.c index c5c9d6c359bb..aefb46778307 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -6,6 +6,7 @@ #include "tailcall_bpf2bpf_hierarchy2.skel.h" #include "tailcall_bpf2bpf_hierarchy3.skel.h" #include "tailcall_freplace.skel.h" +#include "tailcall_freplace_multi.skel.h" #include "tc_bpf2bpf.skel.h" #include "tailcall_fail.skel.h" #include "tailcall_cgrp_storage_owner.skel.h" @@ -1655,6 +1656,77 @@ static void test_tailcall_bpf2bpf_freplace(void) tc_bpf2bpf__destroy(tc_skel); } =20 +static void test_tailcall_freplace_multi(void) +{ + struct tailcall_freplace_multi *freplace_skel =3D NULL; + struct bpf_link *link_subprog =3D NULL, *link_entry =3D NULL; + struct tc_bpf2bpf *tc_skel =3D NULL; + int tc_prog_fd, map_fd, key =3D 0, err; + + tc_skel =3D tc_bpf2bpf__open_and_load(); + if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load")) + return; + + tc_prog_fd =3D bpf_program__fd(tc_skel->progs.entry_tc); + + freplace_skel =3D tailcall_freplace_multi__open(); + if (!ASSERT_OK_PTR(freplace_skel, "tailcall_freplace_multi__open")) + goto out; + + err =3D bpf_program__set_attach_target(freplace_skel->progs.subprog_frepl= ace, + tc_prog_fd, "subprog_tc"); + if (!ASSERT_OK(err, "set_attach_target subprog_tc")) + goto out; + + err =3D bpf_program__set_attach_target(freplace_skel->progs.entry_freplac= e, + tc_prog_fd, "entry_tc"); + if (!ASSERT_OK(err, "set_attach_target entry_tc")) + goto out; + + err =3D tailcall_freplace_multi__load(freplace_skel); + if (!ASSERT_OK(err, "tailcall_freplace_multi__load")) + goto out; + + map_fd =3D bpf_map__fd(freplace_skel->maps.jmp_table); + + link_subprog =3D bpf_program__attach_freplace(freplace_skel->progs.subpro= g_freplace, + tc_prog_fd, "subprog_tc"); + if (!ASSERT_OK_PTR(link_subprog, "attach_freplace subprog_tc")) + goto out; + + link_entry =3D bpf_program__attach_freplace(freplace_skel->progs.entry_fr= eplace, + tc_prog_fd, "entry_tc"); + if (!ASSERT_OK_PTR(link_entry, "attach_freplace entry_tc")) + goto out; + + err =3D bpf_map_update_elem(map_fd, &key, &tc_prog_fd, BPF_ANY); + if (!ASSERT_ERR(err, "update jmp_table with extended prog")) + goto out; + + err =3D bpf_link__destroy(link_entry); + link_entry =3D NULL; + if (!ASSERT_OK(err, "destroy entry link")) + goto out; + + err =3D bpf_map_update_elem(map_fd, &key, &tc_prog_fd, BPF_ANY); + if (!ASSERT_ERR(err, "update jmp_table with still extended prog")) + goto out; + + err =3D bpf_link__destroy(link_subprog); + link_subprog =3D NULL; + if (!ASSERT_OK(err, "destroy subprog link")) + goto out; + + err =3D bpf_map_update_elem(map_fd, &key, &tc_prog_fd, BPF_ANY); + ASSERT_OK(err, "update jmp_table"); + +out: + bpf_link__destroy(link_subprog); + bpf_link__destroy(link_entry); + tailcall_freplace_multi__destroy(freplace_skel); + tc_bpf2bpf__destroy(tc_skel); +} + static void test_tailcall_failure() { RUN_TESTS(tailcall_fail); @@ -2005,6 +2077,8 @@ void test_tailcalls(void) test_tailcall_freplace(); if (test__start_subtest("tailcall_bpf2bpf_freplace")) test_tailcall_bpf2bpf_freplace(); + if (test__start_subtest("tailcall_freplace_multi")) + test_tailcall_freplace_multi(); if (test__start_subtest("tailcall_failure")) test_tailcall_failure(); if (test__start_subtest("tailcall_sleepable")) diff --git a/tools/testing/selftests/bpf/progs/tailcall_freplace_multi.c b/= tools/testing/selftests/bpf/progs/tailcall_freplace_multi.c new file mode 100644 index 000000000000..ca764411dc0b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_freplace_multi.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 KylinSoft Corporation. */ + +#include +#include + +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"); + +SEC("freplace") +int subprog_freplace(struct __sk_buff *skb) +{ + bpf_tail_call_static(skb, &jmp_table, 0); + return 0; +} + +SEC("freplace") +int entry_freplace(struct __sk_buff *skb) +{ + return 0; +} + +char __license[] SEC("license") =3D "GPL"; --=20 2.54.0