* [PATCH bpf 2/2] selftests/bpf: Add test for btf__raw_data() after btf__relocate()
2026-11-01 15:56 [PATCH bpf 1/2] libbpf: Fix stale raw data after btf__relocate() Jiayuan Chen
@ 2026-11-01 15:56 ` Jiayuan Chen
2026-11-01 18:02 ` [PATCH bpf 1/2] libbpf: Fix stale raw data " Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: Jiayuan Chen @ 2026-11-01 15:56 UTC (permalink / raw)
To: bpf
Cc: Jiayuan Chen, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Shuah Khan, Alan Maguire,
linux-kernel, linux-kselftest
Populate the raw data cache, relocate split BTF to a base where the
referenced type has a different id, and check the serialized BTF
carries the new id.
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
.../selftests/bpf/prog_tests/btf_distill.c | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
index f0900b47fd48..7f62a747a0ae 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -601,6 +601,68 @@ static void test_distilled_endianness(void)
btf__free(base);
}
+/*
+ * Raw data cached by btf__raw_data() before btf__relocate() must not be
+ * returned afterwards; serialized split BTF should carry relocated ids.
+ */
+static void test_distilled_relocate_raw_data(void)
+{
+ struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL;
+ const void *raw_data;
+ __u32 size;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+ btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+ btf__add_ptr(btf2, 1); /* [2] ptr to int */
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1");
+
+ /* populate raw data cache before relocation */
+ raw_data = btf__raw_data(btf2, &size);
+ if (!ASSERT_OK_PTR(raw_data, "btf__raw_data #1"))
+ goto cleanup;
+
+ /* new base where int has a different type id */
+ btf3 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf3, "empty_new_base_btf"))
+ goto cleanup;
+ btf__add_int(btf3, "padding", 4, BTF_INT_SIGNED); /* [1] padding */
+ btf__add_int(btf3, "int", 4, BTF_INT_SIGNED); /* [2] int */
+
+ if (!ASSERT_EQ(btf__relocate(btf2, btf3), 0, "relocate_split"))
+ goto cleanup;
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'padding' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[3] PTR '(anon)' type_id=2");
+
+ /* serialized split BTF must match relocated in-memory BTF */
+ raw_data = btf__raw_data(btf2, &size);
+ if (!ASSERT_OK_PTR(raw_data, "btf__raw_data #2"))
+ goto cleanup;
+ btf4 = btf__new_split(raw_data, size, btf3);
+ if (!ASSERT_OK_PTR(btf4, "btf__new_split"))
+ goto cleanup;
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'padding' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[3] PTR '(anon)' type_id=2");
+cleanup:
+ btf__free(btf4);
+ btf__free(btf3);
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
/* If a needed composite type, which is the member of composite type
* in the split BTF, has a different size in the base BTF we wish to
* relocate with, btf__relocate() should error out.
@@ -793,6 +855,8 @@ void test_btf_distill(void)
test_distilled_base_vmlinux();
if (test__start_subtest("distilled_endianness"))
test_distilled_endianness();
+ if (test__start_subtest("distilled_relocate_raw_data"))
+ test_distilled_relocate_raw_data();
if (test__start_subtest("distilled_loc"))
test_distilled_loc();
}
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH bpf 1/2] libbpf: Fix stale raw data after btf__relocate()
2026-11-01 15:56 [PATCH bpf 1/2] libbpf: Fix stale raw data after btf__relocate() Jiayuan Chen
2026-11-01 15:56 ` [PATCH bpf 2/2] selftests/bpf: Add test for btf__raw_data() " Jiayuan Chen
@ 2026-11-01 18:02 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: Alexei Starovoitov @ 2026-11-01 18:02 UTC (permalink / raw)
To: Jiayuan Chen, bpf
Cc: 洛, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
Shuah Khan, Alan Maguire, linux-kernel, linux-kselftest
On Sun, Nov 01, 2026 at 07:56 AM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
> Call btf_ensure_modifiable() first, like other BTF mutators do, so the
> cache is invalidated.
btf_parse_elf() calls btf__relocate() for every module with .BTF.base.
That BTF is read-only. types_data points into raw_data, so raw_data is
not a cache there. Only raw_data_swapped can be stale.
With this patch that path changes:
today with the patch
types rewritten in place copied
strings as-is copied and hashed into strset
named_start_id kept 0, lookup by name is linear
has_hdr_extra relocates -EOPNOTSUPP
The last row: btf__parse_split() of a module with new header fields fails.
Ihor removes the same side effect from btf__find_str():
https://lore.kernel.org/bpf/20261030233355.1836749-2-ihor.solodrai@linux.dev/
can we drop only the copies ?
The kernel calls btf_check_sorted() after btf_relocate() too.
Something like this (not even compile tested):
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index d2ff6f5..d67f78d 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -2033,7 +2033,8 @@ struct btf *btf__load_from_kernel_by_id(__u32 id)
static void btf_invalidate_raw_data(struct btf *btf)
{
- if (btf->raw_data)
+ /* types_data and strs_data of read-only BTF point into raw_data */
+ if (btf->raw_data && btf_is_modifiable(btf))
btf_free_raw_data(btf);
if (btf->raw_data_swapped) {
free(btf->raw_data_swapped);
@@ -2099,11 +2100,11 @@ static int btf_ensure_modifiable(struct btf *btf)
if (!btf->base_btf && btf->hdr.str_len == 1)
btf->strs_deduped = true;
+ btf->modifiable = true;
+
/* invalidate raw_data representation */
btf_invalidate_raw_data(btf);
- btf->modifiable = true;
-
return 0;
err_out:
@@ -6642,8 +6643,12 @@ int btf__relocate(struct btf *btf, const struct btf *base_btf)
{
int err = btf_relocate(btf, base_btf, NULL, NULL);
- if (!err)
+ /* types are rewritten in place, some of them even on error */
+ btf_invalidate_raw_data(btf);
+ if (!err) {
btf->owns_base = false;
+ btf_check_sorted(btf);
+ }
return libbpf_err(err);
}
Also pls target bpf-next. The selftest is contrived.
pw-bot: cr
^ permalink raw reply related [flat|nested] 3+ messages in thread