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
177 changes: 167 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,170 @@ 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)
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 BTF type ordering by name and counts named types.
*
* Checks that types are sorted in ascending order with named types
* before anonymous ones. If verified, sets nr_sorted_types to the
* number of named types.
*/
static void btf_check_sorted(struct btf *btf, int start_id)
{
const struct btf_type *t;
int i, n, nr_sorted_types;

if (likely(btf->nr_sorted_types != BTF_NEED_SORT_CHECK))
return;

btf->nr_sorted_types = 0;

if (btf->nr_types < 2)
return;

nr_sorted_types = 0;
n = btf_nr_types(btf);
for (n--, i = start_id; i < n; i++) {
int k = i + 1;

if (btf_compare_type_names(&i, &k, btf) > 0)
return;

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

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

if (nr_sorted_types)
btf->nr_sorted_types = nr_sorted_types;
}

/* Find BTF types with matching names within the [left, right] index range.
* On success, updates *left and *right to the boundaries of the matching range
* and returns the leftmost matching index.
*/
static s32 btf_find_by_name_kind_bsearch(const struct btf *btf, const char *name,
s32 *left, s32 *right)
{
const struct btf_type *t;
const char *tname;
u32 i, total;
s32 l, r, m, lmost, rmost;
int ret;

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;
/* found the leftmost btf_type that matches */
l = *left;
r = *right;
lmost = -1;
while (l <= r) {
m = l + (r - l) / 2;
t = btf_type_by_id(btf, m);
tname = btf_name_by_offset(btf, t->name_off);
ret = strcmp(tname, name);
if (ret < 0) {
l = m + 1;
} else {
if (ret == 0)
lmost = m;
r = m - 1;
}
}

if (lmost == -1)
return -ENOENT;

/* found the rightmost btf_type that matches */
l = lmost;
r = *right;
rmost = -1;
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) {
if (ret == 0)
rmost = m;
l = m + 1;
} else {
r = m - 1;
}
}

return -ENOENT;
*left = lmost;
*right = rmost;
return lmost;
}

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 == -ENOENT) {
btf_check_sorted((struct btf *)btf, btf_start_id(btf));

if (btf->nr_sorted_types) {
/* binary search */
s32 l, r;
int ret;

l = btf_start_id(btf);
r = l + btf->nr_sorted_types - 1;
ret = btf_find_by_name_kind_bsearch(btf, name, &l, &r);
if (ret < 0)
goto out;
/* found the leftmost btf_type that matches */
while (l <= r) {
t = btf_type_by_id(btf, l);
if (BTF_INFO_KIND(t->info) == kind)
return l;
l++;
}
} 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 +5945,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 +6365,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 +6483,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