Skip to content

Commit 865ce09

Browse files
kkdwvdAlexei Starovoitov
authored andcommitted
bpf: Verify ownership relationships for user BTF types
Ensure that there can be no ownership cycles among different types by way of having owning objects that can hold some other type as their element. For instance, a map value can only hold allocated objects, but these are allowed to have another bpf_list_head. To prevent unbounded recursion while freeing resources, elements of bpf_list_head in local kptrs can never have a bpf_list_head which are part of list in a map value. Later patches will verify this by having dedicated BTF selftests. Also, to make runtime destruction easier, once btf_struct_metas is fully populated, we can stash the metadata of the value type directly in the metadata of the list_head fields, as that allows easier access to the value type's layout to destruct it at runtime from the btf_field entry of the list head itself. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20221118015614.2013203-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 8ffa5cc commit 865ce09

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ struct btf_field_list_head {
191191
struct btf *btf;
192192
u32 value_btf_id;
193193
u32 node_offset;
194+
struct btf_record *value_rec;
194195
};
195196

196197
struct btf_field {

include/linux/btf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t);
178178
int btf_find_timer(const struct btf *btf, const struct btf_type *t);
179179
struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t,
180180
u32 field_mask, u32 value_size);
181+
int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec);
181182
struct btf_field_offs *btf_parse_field_offs(struct btf_record *rec);
182183
bool btf_type_is_void(const struct btf_type *t);
183184
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind);

kernel/bpf/btf.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3723,6 +3723,67 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
37233723
return ERR_PTR(ret);
37243724
}
37253725

3726+
int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
3727+
{
3728+
int i;
3729+
3730+
/* There are two owning types, kptr_ref and bpf_list_head. The former
3731+
* only supports storing kernel types, which can never store references
3732+
* to program allocated local types, atleast not yet. Hence we only need
3733+
* to ensure that bpf_list_head ownership does not form cycles.
3734+
*/
3735+
if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & BPF_LIST_HEAD))
3736+
return 0;
3737+
for (i = 0; i < rec->cnt; i++) {
3738+
struct btf_struct_meta *meta;
3739+
u32 btf_id;
3740+
3741+
if (!(rec->fields[i].type & BPF_LIST_HEAD))
3742+
continue;
3743+
btf_id = rec->fields[i].list_head.value_btf_id;
3744+
meta = btf_find_struct_meta(btf, btf_id);
3745+
if (!meta)
3746+
return -EFAULT;
3747+
rec->fields[i].list_head.value_rec = meta->record;
3748+
3749+
if (!(rec->field_mask & BPF_LIST_NODE))
3750+
continue;
3751+
3752+
/* We need to ensure ownership acyclicity among all types. The
3753+
* proper way to do it would be to topologically sort all BTF
3754+
* IDs based on the ownership edges, since there can be multiple
3755+
* bpf_list_head in a type. Instead, we use the following
3756+
* reasoning:
3757+
*
3758+
* - A type can only be owned by another type in user BTF if it
3759+
* has a bpf_list_node.
3760+
* - A type can only _own_ another type in user BTF if it has a
3761+
* bpf_list_head.
3762+
*
3763+
* We ensure that if a type has both bpf_list_head and
3764+
* bpf_list_node, its element types cannot be owning types.
3765+
*
3766+
* To ensure acyclicity:
3767+
*
3768+
* When A only has bpf_list_head, ownership chain can be:
3769+
* A -> B -> C
3770+
* Where:
3771+
* - B has both bpf_list_head and bpf_list_node.
3772+
* - C only has bpf_list_node.
3773+
*
3774+
* When A has both bpf_list_head and bpf_list_node, some other
3775+
* type already owns it in the BTF domain, hence it can not own
3776+
* another owning type through any of the bpf_list_head edges.
3777+
* A -> B
3778+
* Where:
3779+
* - B only has bpf_list_node.
3780+
*/
3781+
if (meta->record->field_mask & BPF_LIST_HEAD)
3782+
return -ELOOP;
3783+
}
3784+
return 0;
3785+
}
3786+
37263787
static int btf_field_offs_cmp(const void *_a, const void *_b, const void *priv)
37273788
{
37283789
const u32 a = *(const u32 *)_a;
@@ -5413,6 +5474,16 @@ static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size,
54135474
}
54145475
btf->struct_meta_tab = struct_meta_tab;
54155476

5477+
if (struct_meta_tab) {
5478+
int i;
5479+
5480+
for (i = 0; i < struct_meta_tab->cnt; i++) {
5481+
err = btf_check_and_fixup_fields(btf, struct_meta_tab->types[i].record);
5482+
if (err < 0)
5483+
goto errout_meta;
5484+
}
5485+
}
5486+
54165487
if (log->level && bpf_verifier_log_full(log)) {
54175488
err = -ENOSPC;
54185489
goto errout_meta;

kernel/bpf/syscall.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,10 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
10541054
}
10551055
}
10561056

1057+
ret = btf_check_and_fixup_fields(btf, map->record);
1058+
if (ret < 0)
1059+
goto free_map_tab;
1060+
10571061
if (map->ops->map_check_btf) {
10581062
ret = map->ops->map_check_btf(map, btf, key_type, value_type);
10591063
if (ret < 0)

0 commit comments

Comments
 (0)