> diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c 
> b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c

[ ... ]

> @@ -0,0 +1,256 @@
> +static __u32 *get_ids(const char * const funcs[], int funcs_cnt, const char 
> *mod)
> +{
> +     struct btf *btf, *vmlinux_btf = NULL;
> +     __u32 nr, type_id, cnt = 0;
> +     void *root = NULL;
> +     __u32 *ids = NULL;
> +     int i, err = 0;
> +
> +     btf = btf__load_vmlinux_btf();
> +     if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf"))
> +             return NULL;
> +
> +     if (mod) {
> +             vmlinux_btf = btf;
> +             btf = btf__load_module_btf(mod, vmlinux_btf);
> +             if (!ASSERT_OK_PTR(btf, "btf__load_module_btf")) {
> +                     btf__free(vmlinux_btf);
> +                     goto out;
> +             }
> +     }

[ ... ]

> +out:
> +     tdestroy(root, tdestroy_free_nop);
> +     btf__free(vmlinux_btf);
> +     btf__free(btf);
> +     return ids;
> +}

Does this code double-free vmlinux_btf when btf__load_module_btf() fails?
If btf__load_module_btf(mod, vmlinux_btf) fails, the error path at
ASSERT_OK_PTR calls btf__free(vmlinux_btf) and then jumps to the out:
label, which calls btf__free(vmlinux_btf) again on the same pointer.

The first free happens here:

        if (mod) {
                vmlinux_btf = btf;
                btf = btf__load_module_btf(mod, vmlinux_btf);
                if (!ASSERT_OK_PTR(btf, "btf__load_module_btf")) {
                        btf__free(vmlinux_btf);  <-- FIRST FREE
                        goto out;
                }
        }

Then at the out: label:

out:
        tdestroy(root, tdestroy_free_nop);
        btf__free(vmlinux_btf);  <-- SECOND FREE
        btf__free(btf);

The btf__free() implementation in tools/lib/bpf/btf.c checks only
IS_ERR_OR_NULL(btf) and does not guard against already-freed memory,
so this would trigger a double-free that glibc would detect and abort.

In the current commit, get_ids() is only called with mod=NULL from
test_link_api_ids(), so this path is latent. However, a subsequent commit
(4dda98b9e4d0, "selftests/bpf: Add tracing multi skel/pattern/ids module
attach tests") adds a caller get_ids(bpf_testmod_fentry_test, cnt,
"bpf_testmod") that activates the buggy path.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/24583317711

Reply via email to