Skip to content

Commit 7a851ec

Browse files
davemarchevskyAlexei Starovoitov
authored andcommitted
bpf: Search for kptrs in prog BTF structs
Currently btf_parse_fields is used in two places to create struct btf_record's for structs: when looking at mapval type, and when looking at any struct in program BTF. The former looks for kptr fields while the latter does not. This patch modifies the btf_parse_fields call made when looking at prog BTF struct types to search for kptrs as well. Before this series there was no reason to search for kptrs in non-mapval types: a referenced kptr needs some owner to guarantee resource cleanup, and map values were the only owner that supported this. If a struct with a kptr field were to have some non-kptr-aware owner, the kptr field might not be properly cleaned up and result in resources leaking. Only searching for kptr fields in mapval was a simple way to avoid this problem. In practice, though, searching for BPF_KPTR when populating struct_meta_tab does not expose us to this risk, as struct_meta_tab is only accessed through btf_find_struct_meta helper, and that helper is only called in contexts where recognizing the kptr field is safe: * PTR_TO_BTF_ID reg w/ MEM_ALLOC flag * Such a reg is a local kptr and must be free'd via bpf_obj_drop, which will correctly handle kptr field * When handling specific kfuncs which either expect MEM_ALLOC input or return MEM_ALLOC output (obj_{new,drop}, percpu_obj_{new,drop}, list+rbtree funcs, refcount_acquire) * Will correctly handle kptr field for same reasons as above * When looking at kptr pointee type * Called by functions which implement "correct kptr resource handling" * In btf_check_and_fixup_fields * Helper that ensures no ownership loops for lists and rbtrees, doesn't care about kptr field existence So we should be able to find BPF_KPTR fields in all prog BTF structs without leaking resources. Further patches in the series will build on this change to support kptr_xchg into non-mapval local kptr. Without this change there would be no kptr field found in such a type. Acked-by: Martin KaFai Lau <[email protected]> Acked-by: Hou Tao <[email protected]> Signed-off-by: Dave Marchevsky <[email protected]> Signed-off-by: Amery Hung <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent c5ef534 commit 7a851ec

File tree

1 file changed

+52
-18
lines changed

1 file changed

+52
-18
lines changed

kernel/bpf/btf.c

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5512,36 +5512,70 @@ static const char *alloc_obj_fields[] = {
55125512
static struct btf_struct_metas *
55135513
btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
55145514
{
5515-
union {
5516-
struct btf_id_set set;
5517-
struct {
5518-
u32 _cnt;
5519-
u32 _ids[ARRAY_SIZE(alloc_obj_fields)];
5520-
} _arr;
5521-
} aof;
55225515
struct btf_struct_metas *tab = NULL;
5516+
struct btf_id_set *aof;
55235517
int i, n, id, ret;
55245518

55255519
BUILD_BUG_ON(offsetof(struct btf_id_set, cnt) != 0);
55265520
BUILD_BUG_ON(sizeof(struct btf_id_set) != sizeof(u32));
55275521

5528-
memset(&aof, 0, sizeof(aof));
5522+
aof = kmalloc(sizeof(*aof), GFP_KERNEL | __GFP_NOWARN);
5523+
if (!aof)
5524+
return ERR_PTR(-ENOMEM);
5525+
aof->cnt = 0;
5526+
55295527
for (i = 0; i < ARRAY_SIZE(alloc_obj_fields); i++) {
55305528
/* Try to find whether this special type exists in user BTF, and
55315529
* if so remember its ID so we can easily find it among members
55325530
* of structs that we iterate in the next loop.
55335531
*/
5532+
struct btf_id_set *new_aof;
5533+
55345534
id = btf_find_by_name_kind(btf, alloc_obj_fields[i], BTF_KIND_STRUCT);
55355535
if (id < 0)
55365536
continue;
5537-
aof.set.ids[aof.set.cnt++] = id;
5537+
5538+
new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]),
5539+
GFP_KERNEL | __GFP_NOWARN);
5540+
if (!new_aof) {
5541+
ret = -ENOMEM;
5542+
goto free_aof;
5543+
}
5544+
aof = new_aof;
5545+
aof->ids[aof->cnt++] = id;
5546+
}
5547+
5548+
n = btf_nr_types(btf);
5549+
for (i = 1; i < n; i++) {
5550+
/* Try to find if there are kptrs in user BTF and remember their ID */
5551+
struct btf_id_set *new_aof;
5552+
struct btf_field_info tmp;
5553+
const struct btf_type *t;
5554+
5555+
t = btf_type_by_id(btf, i);
5556+
if (!t) {
5557+
ret = -EINVAL;
5558+
goto free_aof;
5559+
}
5560+
5561+
ret = btf_find_kptr(btf, t, 0, 0, &tmp);
5562+
if (ret != BTF_FIELD_FOUND)
5563+
continue;
5564+
5565+
new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]),
5566+
GFP_KERNEL | __GFP_NOWARN);
5567+
if (!new_aof) {
5568+
ret = -ENOMEM;
5569+
goto free_aof;
5570+
}
5571+
aof = new_aof;
5572+
aof->ids[aof->cnt++] = i;
55385573
}
55395574

5540-
if (!aof.set.cnt)
5575+
if (!aof->cnt)
55415576
return NULL;
5542-
sort(&aof.set.ids, aof.set.cnt, sizeof(aof.set.ids[0]), btf_id_cmp_func, NULL);
5577+
sort(&aof->ids, aof->cnt, sizeof(aof->ids[0]), btf_id_cmp_func, NULL);
55435578

5544-
n = btf_nr_types(btf);
55455579
for (i = 1; i < n; i++) {
55465580
struct btf_struct_metas *new_tab;
55475581
const struct btf_member *member;
@@ -5551,17 +5585,13 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
55515585
int j, tab_cnt;
55525586

55535587
t = btf_type_by_id(btf, i);
5554-
if (!t) {
5555-
ret = -EINVAL;
5556-
goto free;
5557-
}
55585588
if (!__btf_type_is_struct(t))
55595589
continue;
55605590

55615591
cond_resched();
55625592

55635593
for_each_member(j, t, member) {
5564-
if (btf_id_set_contains(&aof.set, member->type))
5594+
if (btf_id_set_contains(aof, member->type))
55655595
goto parse;
55665596
}
55675597
continue;
@@ -5580,7 +5610,8 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
55805610
type = &tab->types[tab->cnt];
55815611
type->btf_id = i;
55825612
record = btf_parse_fields(btf, t, BPF_SPIN_LOCK | BPF_LIST_HEAD | BPF_LIST_NODE |
5583-
BPF_RB_ROOT | BPF_RB_NODE | BPF_REFCOUNT, t->size);
5613+
BPF_RB_ROOT | BPF_RB_NODE | BPF_REFCOUNT |
5614+
BPF_KPTR, t->size);
55845615
/* The record cannot be unset, treat it as an error if so */
55855616
if (IS_ERR_OR_NULL(record)) {
55865617
ret = PTR_ERR_OR_ZERO(record) ?: -EFAULT;
@@ -5589,9 +5620,12 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
55895620
type->record = record;
55905621
tab->cnt++;
55915622
}
5623+
kfree(aof);
55925624
return tab;
55935625
free:
55945626
btf_struct_metas_free(tab);
5627+
free_aof:
5628+
kfree(aof);
55955629
return ERR_PTR(ret);
55965630
}
55975631

0 commit comments

Comments
 (0)