Program BTF records can own other program-allocated objects through local referenced kptrs. Releasing such an object follows the kptr through bpf_obj_free_fields() and __bpf_obj_drop_impl(). The ownership graph walk only follows list and rbtree roots because referenced kptrs originally supported kernel types with registered destructors. Support for exchanging program-allocated objects into local kptrs invalidated that assumption. A self-referential local kptr lets a CAP_BPF user build an arbitrarily deep object chain, and dropping its head can exhaust the kernel stack and panic the kernel. A sufficiently long acyclic chain has the same problem. Extend the bounded ownership walk to program-local referenced kptrs. A pointee without special fields is absent from the struct metadata table and adds only a final non-recursing drop. Also include local percpu kptrs even though bpf_percpu_obj_new() currently rejects types with special-field metadata. Rejecting unsafe ownership graphs now keeps the depth invariant intact if that restriction is relaxed later. Kernel-BTF kptrs remain outside the walk because they do not recurse through program BTF records. Fixes: b0966c724584 ("bpf: Support bpf_kptr_xchg into local kptr") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/btf.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 2a458499a236..3c508ebb2438 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -4313,20 +4313,28 @@ static int btf_owned_type_idx(const struct btf *btf, struct btf_struct_metas *ta struct btf_struct_meta *meta; u32 btf_id; - if (!(field->type & BPF_GRAPH_ROOT)) + if (field->type & BPF_GRAPH_ROOT) { + btf_id = field->graph_root.value_btf_id; + } else if (field->type == BPF_KPTR_REF || field->type == BPF_KPTR_PERCPU) { + if (btf_is_kernel(field->kptr.btf)) + return -ENOENT; + btf_id = field->kptr.btf_id; + } else { return -ENOENT; - btf_id = field->graph_root.value_btf_id; + } + meta = btf_find_struct_meta(btf, btf_id); if (!meta) - return -EFAULT; + return field->type & BPF_GRAPH_ROOT ? -EFAULT : -ENOENT; return meta - tab->types; } /* - * Each graph ownership edge adds kernel frames through - * bpf_obj_free_fields() and __bpf_obj_drop_impl(). Keep the bound - * deliberately small because object destruction can itself run below a BPF - * call chain. + * Each ownership edge adds kernel frames through bpf_obj_free_fields() and + * __bpf_obj_drop_impl(). Keep the bound deliberately small because object + * destruction can itself run below a BPF call chain. A final pointee without + * special fields is not present in the struct metadata table and adds only a + * non-recursing drop. */ #define BTF_MAX_OWNERSHIP_DEPTH 8 -- 2.53.0