selftests/bpf: Add BTF sanitize test covering BTF layout

Add test that fakes up a feature cache of supported BPF
features to simulate an older kernel that does not support
BTF layout information.  Ensure that BTF is sanitized correctly
to remove layout info between types and strings, and that all
offsets and lengths are adjusted appropriately.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Link: https://lore.kernel.org/r/20260408165735.843763-3-alan.maguire@oracle.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alan Maguire
2026-04-08 17:57:35 +01:00
committed by Alexei Starovoitov
parent 7419fcadd1
commit 0e4dc6fbdd

View File

@@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026, Oracle and/or its affiliates. */
#include <test_progs.h>
#include <linux/btf.h>
#include "bpf/libbpf_internal.h"
#include "../test_btf.h"
#include "kfree_skb.skel.h"
#define TYPE_LEN (sizeof(struct btf_type) + sizeof(__u32))
#define MAX_NR_LAYOUT 2
#define LAYOUT_LEN (sizeof(struct btf_layout) * MAX_NR_LAYOUT)
#define STR_LEN sizeof("\0int")
struct layout_btf {
struct btf_header hdr;
__u32 types[TYPE_LEN/sizeof(__u32)];
struct btf_layout layout[MAX_NR_LAYOUT];
char strs[STR_LEN];
};
static const struct layout_btf layout_btf = {
.hdr = {
.magic = BTF_MAGIC,
.version = BTF_VERSION,
.hdr_len = sizeof(struct btf_header),
.type_off = 0,
.type_len = TYPE_LEN,
.str_off = TYPE_LEN + LAYOUT_LEN,
.str_len = STR_LEN,
.layout_off = TYPE_LEN,
.layout_len = LAYOUT_LEN,
},
.types = {
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
},
.layout = {
{ .info_sz = 0, .elem_sz = 0, .flags = 0 },
{ .info_sz = sizeof(__u32), .elem_sz = 0, .flags = 0 },
},
.strs = "\0int",
};
void test_btf_sanitize_layout(void)
{
struct btf *orig = NULL, *sanitized = NULL;
struct kern_feature_cache *cache = NULL;
struct kfree_skb *skel = NULL;
const struct btf_header *hdr;
const void *raw;
__u32 raw_sz;
skel = kfree_skb__open();
if (!ASSERT_OK_PTR(skel, "kfree_skb_skel"))
return;
orig = btf__new(&layout_btf, sizeof(layout_btf));
if (!ASSERT_OK_PTR(orig, "btf_new_layout"))
goto out;
raw = btf__raw_data(orig, &raw_sz);
if (!ASSERT_OK_PTR(raw, "btf__raw_data_orig"))
goto out;
hdr = (struct btf_header *)raw;
ASSERT_EQ(hdr->layout_off, TYPE_LEN, "layout_off_nonzero");
ASSERT_EQ(hdr->layout_len, LAYOUT_LEN, "layout_len_nonzero");
cache = calloc(1, sizeof(*cache));
if (!ASSERT_OK_PTR(cache, "alloc_feat_cache"))
goto out;
for (int i = 0; i < __FEAT_CNT; i++)
cache->res[i] = FEAT_SUPPORTED;
cache->res[FEAT_BTF_LAYOUT] = FEAT_MISSING;
bpf_object_set_feat_cache(skel->obj, cache);
if (!ASSERT_FALSE(kernel_supports(skel->obj, FEAT_BTF_LAYOUT), "layout_feature_missing"))
goto out;
if (!ASSERT_TRUE(kernel_supports(skel->obj, FEAT_BTF_FUNC), "other_feature_allowed"))
goto out;
sanitized = bpf_object__sanitize_btf(skel->obj, orig);
if (!ASSERT_OK_PTR(sanitized, "bpf_object__sanitize_btf"))
goto out;
raw = btf__raw_data(sanitized, &raw_sz);
if (!ASSERT_OK_PTR(raw, "btf__raw_data_sanitized"))
goto out;
hdr = (struct btf_header *)raw;
ASSERT_EQ(hdr->layout_off, 0, "layout_off_zero");
ASSERT_EQ(hdr->layout_len, 0, "layout_len_zero");
ASSERT_EQ(hdr->str_off, TYPE_LEN, "strs_after_types");
ASSERT_EQ(hdr->str_len, STR_LEN, "strs_len_unchanged");
ASSERT_EQ(raw_sz, hdr->hdr_len + hdr->type_len + hdr->str_len, "btf_raw_sz_reduced");
out:
/* This will free the cache we allocated above */
kfree_skb__destroy(skel);
btf__free(sanitized);
btf__free(orig);
}