Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 171 additions & 10 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@
*/
#define BTF_MAX_SIZE (16 * 1024 * 1024)

#define BTF_NEED_SORT_CHECK ((u32)-1)

#define for_each_member_from(i, from, struct_type, member) \
for (i = from, member = btf_type_member(struct_type) + from; \
i < btf_type_vlen(struct_type); \
Expand Down Expand Up @@ -259,6 +261,7 @@ struct btf {
void *nohdr_data;
struct btf_header hdr;
u32 nr_types; /* includes VOID for base BTF */
u32 nr_sorted_types; /* exclude VOID for base BTF */
u32 types_size;
u32 data_size;
refcount_t refcnt;
Expand Down Expand Up @@ -494,6 +497,11 @@ static bool btf_type_is_modifier(const struct btf_type *t)
return false;
}

static int btf_start_id(const struct btf *btf)
{
return btf->start_id + (btf->base_btf ? 0 : 1);
}

bool btf_type_is_void(const struct btf_type *t)
{
return t == &btf_void;
Expand Down Expand Up @@ -544,24 +552,174 @@ u32 btf_nr_types(const struct btf *btf)
return total;
}

s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
/* Anonymous types (with empty names) are considered greater than named types
* and are sorted after them. Two anonymous types are considered equal. Named
* types are compared lexicographically.
*/
static int btf_compare_type_names(const void *a, const void *b, void *priv)
{
struct btf *btf = (struct btf *)priv;
const struct btf_type *ta = btf_type_by_id(btf, *(__u32 *)a);
const struct btf_type *tb = btf_type_by_id(btf, *(__u32 *)b);
const char *na, *nb;

if (!ta->name_off && tb->name_off)
return 1;
if (ta->name_off && !tb->name_off)
return -1;
if (!ta->name_off && !tb->name_off)
return 0;

na = btf_name_by_offset(btf, ta->name_off);
nb = btf_name_by_offset(btf, tb->name_off);
return strcmp(na, nb);
}

/* Verifies that BTF types are sorted in ascending order according to their
* names, with named types appearing before anonymous types. If the ordering
* is correct, counts the number of named types and updates the BTF object's
* nr_sorted_types field.
*
* Return: true if types are properly sorted, false otherwise
*/
static bool btf_check_sorted(struct btf *btf)
{
const struct btf_type *t;
const char *tname;
u32 i, total;
int i, n, k = 0, nr_sorted_types;

if (likely(btf->nr_sorted_types != BTF_NEED_SORT_CHECK))
goto out;
btf->nr_sorted_types = 0;

if (btf->nr_types < 2)
goto out;

nr_sorted_types = 0;
n = btf_nr_types(btf) - 1;
for (i = btf_start_id(btf); i < n; i++) {
k = i + 1;
if (btf_compare_type_names(&i, &k, btf) > 0)
goto out;

total = btf_nr_types(btf);
for (i = 1; i < total; i++) {
t = btf_type_by_id(btf, i);
if (BTF_INFO_KIND(t->info) != kind)
continue;
if (t->name_off)
nr_sorted_types++;
}

t = btf_type_by_id(btf, k);
if (t->name_off)
nr_sorted_types++;
if (nr_sorted_types)
btf->nr_sorted_types = nr_sorted_types;

out:
return btf->nr_sorted_types > 0;
}

/* Performs binary search within specified type ID range to find the leftmost
* BTF type matching the given name. The search assumes types are sorted by
* name in lexicographical order within the specified range.
*
* Return: Type ID of leftmost matching type, or -ENOENT if not found
*/
static s32 btf_find_by_name_kind_bsearch(const struct btf *btf, const char *name,
s32 start_id, s32 end_id)
{
const struct btf_type *t;
const char *tname;
s32 l, r, m, lmost = -ENOENT;
int ret;

l = start_id;
r = end_id;
while (l <= r) {
m = l + (r - l) / 2;
t = btf_type_by_id(btf, m);
tname = btf_name_by_offset(btf, t->name_off);
if (!strcmp(tname, name))
return i;
ret = strcmp(tname, name);
if (ret < 0) {
l = m + 1;
} else {
if (ret == 0)
lmost = m;
r = m - 1;
}
}

return -ENOENT;
return lmost;
}

/* Searches for a BTF type with the specified name and kind. The function
* first recursively searches in the base BTF (if present), then searches
* in the current BTF using either binary search (if types are sorted)
* or linear search.
*
* Binary search is used when types are name-sorted (nr_sorted_types > 0).
* After finding a name match, it scans forward to find the first type
* that also matches the specified kind. Linear search is used for unsorted
* types, checking each type sequentially.
*
* Return: Type ID of matching type on success, -ENOENT if not found
*/
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
{
const struct btf *base_btf = btf_base_btf(btf);;
const struct btf_type *t;
const char *tname;
int err = -ENOENT;

if (base_btf) {
err = btf_find_by_name_kind(base_btf, name, kind);
if (err > 0)
goto out;
}

if (btf_check_sorted((struct btf *)btf)) {
/* binary search */
bool skip_first;
s32 start_id, end_id;;
int ret;

start_id = btf_start_id(btf);
end_id = start_id + btf->nr_sorted_types - 1;
ret = btf_find_by_name_kind_bsearch(btf, name, start_id, end_id);
if (ret < 0)
goto out;
skip_first = true;
do {
t = btf_type_by_id(btf, ret);
if (BTF_INFO_KIND(t->info) != kind) {
if (skip_first) {
skip_first = false;
continue;
}
} else if (skip_first) {
return ret;
}
tname = btf_name_by_offset(btf, t->name_off);
if (!strcmp(tname, name))
return ret;
else
break;
} while (++ret <= end_id);
} else {
/* linear search */
u32 i, total;

total = btf_nr_types(btf);
for (i = btf_start_id(btf); i < total; i++) {
t = btf_type_by_id(btf, i);
if (BTF_INFO_KIND(t->info) != kind)
continue;

tname = btf_name_by_offset(btf, t->name_off);
if (!strcmp(tname, name))
return i;
}
}

out:
return err;
}

s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
Expand Down Expand Up @@ -5791,6 +5949,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
goto errout;
}
env->btf = btf;
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;

data = kvmalloc(attr->btf_size, GFP_KERNEL | __GFP_NOWARN);
if (!data) {
Expand Down Expand Up @@ -6210,6 +6369,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name
btf->data = data;
btf->data_size = data_size;
btf->kernel_btf = true;
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
snprintf(btf->name, sizeof(btf->name), "%s", name);

err = btf_parse_hdr(env);
Expand Down Expand Up @@ -6327,6 +6487,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
btf->start_id = base_btf->nr_types;
btf->start_str_off = base_btf->hdr.str_len;
btf->kernel_btf = true;
btf->nr_sorted_types = BTF_NEED_SORT_CHECK;
snprintf(btf->name, sizeof(btf->name), "%s", module_name);

btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
Expand Down
Loading
Loading