Thread (20 messages) 20 messages, 3 authors, 2d ago

Re: [PATCH v3 bpf-next 1/7] resolve_btfids: Build separate main and inline BTF objects

flat view

From: bot+bpf-ci@kernel.org
Date: 2026-10-03 20:27:13
Also in: bpf

resolve_btfids: Build separate main and inline BTF objects
TL;DR: In resolve_btfids --inline, extract_inline_btf() may free a base BTF
that the new main BTF still points to, causing a possible use-after-free for
modules with .BTF.base; in-tree builds seem unaffected.
quoted hunk ↗ jump to hunk
diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 37d7e7224207..dcd289d16faf 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -1510,6 +1569,85 @@ static int btf2btf(struct object *obj)
 	return err;
 }
[ ... ]
quoted hunk ↗ jump to hunk
+static int extract_inline_btf(struct object *obj)
+{
+	struct btf *btf = obj->btf, *main_btf, *inline_btf;
+	__u32 type_cnt = btf__type_cnt(btf);
+	__u8 *colors;
+	int err;
+
+	colors = calloc(type_cnt, sizeof(*colors));
+	if (!colors)
+		return -ENOMEM;
+	err = color_btf(obj, colors);
+	if (err)
+		goto out;
+	err = btf_split_by_color(btf, colors, &main_btf, &inline_btf);
+	if (!err) {
+		obj->btf = main_btf;
+		obj->inline_btf = inline_btf;
+		btf__free(btf);
                 ^^^^

Can this btf__free(btf) free a base BTF that the new obj->btf still points
to?

btf_split_by_color() creates main_btf with .base_btf set to
btf__base_btf(src), and btf_new_empty() stores that pointer without taking
ownership.  btf__free() on the source also frees its base when the source
owns it:

    if (btf->owns_base)
        btf__free(btf->base_btf);

libbpf sets owns_base in btf_parse_elf() when the ELF passed to
btf__parse_split() has a .BTF.base section and no base_btf was given.

This looks reachable with "resolve_btfids --inline mod.ko" (no --btf_base,
no --btf) on an out-of-tree module that already has .BTF, .BTF.base and
.BTF_ids:

load_btf()
  btf__parse_split(obj->path, NULL)
    owns_base = true, obj->base_btf stays NULL
finalize_btf()
  distill is skipped because obj->base_btf is NULL
  sort_btf_by_name()
  extract_inline_btf()
    btf__free(btf)
      also frees the distilled base that main_btf->base_btf points to
main()
  symbols_resolve()
    btf__type_by_id(obj->btf, ...)
      btf_type_by_id(btf->base_btf, type_id)
        reads freed memory

Without --inline the same command works, because obj->btf keeps owning its
base until main() frees it.

The in-tree build does not appear to be affected, since gen-btf.sh passes a
raw --btf file together with --btf_base, which leaves owns_base false.  I did
not find a fix for this later in the series.

Should extract_inline_btf() keep the original BTF alive, for example in a
field of struct object that main() frees after obj->btf?  Alternatively,
could it reject or handle the case where btf__base_btf(obj->btf) is set but
obj->base_btf is NULL?
quoted hunk ↗ jump to hunk
+	}
+out:
+	free(colors);
+	return err;
+}
[ ... ]


---
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/37149955093
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help