* [PATCH bpf] libbpf: Fix use-after-free in btf__add_type
@ 2026-11-02 18:58 Alan Maguire
2026-11-02 19:43 ` bot+bpf-ci
2026-11-02 22:50 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 3+ messages in thread
From: Alan Maguire @ 2026-11-02 18:58 UTC (permalink / raw)
To: ast
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, bpf, Nathan Chancellor, Alan Maguire
From: Alexei Starovoitov <ast@kernel.org>
For every string btf_rewrite_str() does:
off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off));
When the string belongs to the base, the pointer goes into the raw data
of the base. btf__add_str() looks the string up in the base first,
btf__find_str() calls btf_ensure_modifiable() on the base, and that
frees the raw data. Then strset__find_str() reads the freed string.
Make all the bases of dst modifiable before the pointer is taken.
This bug is triggered by the sharing of BTF bases between src
and dst, as introduced by the resolve_btfids changes to build
inline BTF.
Fixes: cf91a7db92c6 ("resolve_btfids: Build separate main and inline BTF objects")
Reported-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alan Maguire <almagui@kernel.org>
Tested-by: Alan Maguire <almagui@kernel.org>
---
tools/lib/bpf/btf.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index d2ff6f5d6d5b..95c31803e722 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -2222,6 +2222,7 @@ struct btf_pipe {
static int btf_rewrite_str(struct btf_pipe *p, __u32 *str_off)
{
+ struct btf *base;
long mapped_off;
int off, err;
@@ -2234,6 +2235,19 @@ static int btf_rewrite_str(struct btf_pipe *p, __u32 *str_off)
return 0;
}
+ /*
+ * btf__add_str() first looks the string up in the base BTFs of dst,
+ * which makes each of them modifiable and frees its raw data. If src
+ * shares a base with dst and the string lives in that base, the
+ * pointer taken below would be left dangling. So make the bases
+ * modifiable before the pointer is taken.
+ */
+ for (base = p->dst->base_btf; base; base = base->base_btf) {
+ err = btf_ensure_modifiable(base);
+ if (err)
+ return err;
+ }
+
off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off));
if (off < 0)
return off;
--
2.56.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH bpf] libbpf: Fix use-after-free in btf__add_type
2026-11-02 18:58 [PATCH bpf] libbpf: Fix use-after-free in btf__add_type Alan Maguire
@ 2026-11-02 19:43 ` bot+bpf-ci
2026-11-02 22:50 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 3+ messages in thread
From: bot+bpf-ci @ 2026-11-02 19:43 UTC (permalink / raw)
To: almagui, ast
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, bpf, nathan, almagui, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3453 bytes --]
> libbpf: Fix use-after-free in btf__add_type
TL;DR: The fix in btf_rewrite_str() may still leave a dangling string pointer
(use-after-free) in btf__add_str() for mid-string offsets in a shared base
BTF; likely not hit by in-tree BTF, so a gap in the fix.
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index d2ff6f5d6d5b..95c31803e722 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
[ ... ]
> @@ -2234,6 +2235,19 @@ static int btf_rewrite_str(struct btf_pipe *p, __u32 *str_off)
> return 0;
> }
>
> + /*
> + * btf__add_str() first looks the string up in the base BTFs of dst,
> + * which makes each of them modifiable and frees its raw data. If src
> + * shares a base with dst and the string lives in that base, the
> + * pointer taken below would be left dangling. So make the bases
> + * modifiable before the pointer is taken.
> + */
> + for (base = p->dst->base_btf; base; base = base->base_btf) {
> + err = btf_ensure_modifiable(base);
> + if (err)
> + return err;
> + }
> +
> off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off));
Can the pointer passed to btf__add_str() here still end up dangling when
*str_off points into the middle of a string in the shared base (a suffix
such as "bar" in "foobar")?
A base that was not modifiable before the loop gets its strset built by
strset__new(), which sets strs_data_cap equal to strs_data_len. The first
lookup in that set therefore reallocs the buffer:
btf__add_str(dst, s)
btf__find_str(base, s)
strset__find_str(base->strs_set, s)
strset_str_append()
strset_add_str_mem() -> libbpf_add_mem() -> realloc(strs_data)
strset_str_append() fixes up its own local copy of s, but the s held by
btf__add_str() is not updated.
Only string starts are hashed in the base, so a suffix offset can miss. In
that case btf__find_str() returns -ENOENT and btf__add_str() carries on with
the original, now stale, pointer:
if (btf->base_btf) {
off = btf__find_str(btf->base_btf, s);
if (off != -ENOENT)
return off;
}
...
off = strset__add_str(btf->strs_set, s);
strset__add_str() -> strset_str_append() then does strlen(s) and memcpy()
from the freed buffer. The relocation check in strset_str_append() does not
help there, since s is not inside dst's buffer.
btf_validate_str() only checks that btf__str_by_offset() is non-NULL, so such
offsets are accepted. This needs src and dst to share a base (or src to be a
base of dst), a base that was not modifiable yet, a first lookup on it with
a mid-string offset, and a realloc that moves the block.
BTF from pahole, LLVM and btf__dedup() does not use suffix offsets, so the
in-tree resolve_btfids flow is probably not affected, and the code before
this patch crashed on the same input. This looks like a gap left by the fix
rather than new breakage.
Would it be more robust to leave *str_off unchanged for offsets that fall
inside a base shared by src and dst, the way btf__add_btf() does with its
"*str_off < src_btf->start_str_off" check ("don't remap strings from shared
base BTF")? The offset is already valid in dst, and no pointer into the base
would be taken at all.
---
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/41508558529
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH bpf] libbpf: Fix use-after-free in btf__add_type
2026-11-02 18:58 [PATCH bpf] libbpf: Fix use-after-free in btf__add_type Alan Maguire
2026-11-02 19:43 ` bot+bpf-ci
@ 2026-11-02 22:50 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-11-02 22:50 UTC (permalink / raw)
To: Alan Maguire
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, bpf, nathan
Hello:
This patch was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Mon, 2 Nov 2026 18:58:39 +0000 you wrote:
> From: Alexei Starovoitov <ast@kernel.org>
>
> For every string btf_rewrite_str() does:
>
> off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off));
>
> When the string belongs to the base, the pointer goes into the raw data
> of the base. btf__add_str() looks the string up in the base first,
> btf__find_str() calls btf_ensure_modifiable() on the base, and that
> frees the raw data. Then strset__find_str() reads the freed string.
>
> [...]
Here is the summary with links:
- [bpf] libbpf: Fix use-after-free in btf__add_type
https://git.kernel.org/bpf/bpf/c/9abe2c42ecab
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-02 22:50 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 18:58 [PATCH bpf] libbpf: Fix use-after-free in btf__add_type Alan Maguire
2026-11-02 19:43 ` bot+bpf-ci
2026-11-02 22:50 ` patchwork-bot+netdevbpf
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.