[PATCH bpf-next 07/13] bpf: parse the vmlinux BTF before taking cand_cache_mutex
From: Jay Wang
Date: Sat Oct 31 2026 - 09:15:12 EST
From: Ihor Solodrai <ihor.solodrai@xxxxxxxxx>
bpf_core_find_cands() calls bpf_get_btf_vmlinux() with cand_cache_mutex
held, from btf_get_ptr_to_btf_id() and bpf_core_apply(). Once the
vmlinux BTF is parsed on first use (later in this series), parsing also
parses the module BTFs kept until then, under btf_module_mutex, while a
module going away takes btf_module_mutex and then cand_cache_mutex
(purge_cand_cache()). That would be an ABBA deadlock:
cand_cache_mutex -> btf_vmlinux_lock -> btf_module_mutex
btf_module_mutex -> cand_cache_mutex
Have both callers fetch the BTF before they take cand_cache_mutex, and
make bpf_core_find_cands() only peek with bpf_peek_btf_vmlinux(). The
ordering was found by design review. With the whole series applied
and the fetch moved back under the mutex, PROVE_LOCKING reports this
cycle at the first rmmod of a BTF module after a kernel CO-RE load
parsed the BTF; with this patch the same sequence is clean.
No functional change: the BTF is parsed at the same points, just
before the mutex is taken; a broken BTF fails the load with the error
of its parse, and bpf_core_find_cands() handles a missing BTF as before.
TODO-NEEDS-IHOR: Signed-off-by: Ihor Solodrai <ihor.solodrai@xxxxxxxxx>
[pass the error of the fetch on instead of -EINVAL from the peek]
Signed-off-by: Jay Wang <wanjay@xxxxxxxxxx>
---
kernel/bpf/btf.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 59d6f44cbe17..70adc52f5d7c 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8596,6 +8596,7 @@ static int btf_get_ptr_to_btf_id(struct bpf_verifier_log *log, int arg_idx,
.btf = btf,
.log = log,
};
+ struct btf *vmlinux_btf;
u32 kern_type_id, type_id;
int err = 0;
@@ -8607,6 +8608,10 @@ static int btf_get_ptr_to_btf_id(struct bpf_verifier_log *log, int arg_idx,
t = btf_type_by_id(btf, t->type);
}
+ /* parse it, if this is its first use, before taking cand_cache_mutex */
+ vmlinux_btf = bpf_get_btf_vmlinux();
+ if (IS_ERR(vmlinux_btf))
+ return PTR_ERR(vmlinux_btf);
mutex_lock(&cand_cache_mutex);
cc = bpf_core_find_cands(&ctx, type_id);
if (IS_ERR(cc)) {
@@ -10610,7 +10615,8 @@ bpf_core_find_cands(struct bpf_core_ctx *ctx, u32 local_type_id)
const char *name;
int id;
- main_btf = bpf_get_btf_vmlinux();
+ /* callers parse it before taking cand_cache_mutex, see bpf_core_apply() */
+ main_btf = bpf_peek_btf_vmlinux();
if (IS_ERR(main_btf))
return ERR_CAST(main_btf);
if (!main_btf)
@@ -10713,8 +10719,18 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
if (need_cands) {
struct bpf_cand_cache *cc;
+ struct btf *main_btf;
int i;
+ /*
+ * Parsing the vmlinux BTF takes btf_module_mutex, which is
+ * taken before cand_cache_mutex when a module goes away.
+ */
+ main_btf = bpf_get_btf_vmlinux();
+ if (IS_ERR(main_btf)) {
+ kfree(specs);
+ return PTR_ERR(main_btf);
+ }
mutex_lock(&cand_cache_mutex);
cc = bpf_core_find_cands(ctx, relo->type_id);
if (IS_ERR(cc)) {
--
2.47.3