bpf: Populate pairs of btf_id and destructor kfunc in btf
To support storing referenced PTR_TO_BTF_ID in maps, we require associating a specific BTF ID with a 'destructor' kfunc. This is because we need to release a live referenced pointer at a certain offset in map value from the map destruction path, otherwise we end up leaking resources. Hence, introduce support for passing an array of btf_id, kfunc_btf_id pairs that denote a BTF ID and its associated release function. Then, add an accessor 'btf_find_dtor_kfunc' which can be used to look up the destructor kfunc of a certain BTF ID. If found, we can use it to free the object from the map free path. The registration of these pairs also serve as a whitelist of structures which are allowed as referenced PTR_TO_BTF_ID in a BPF map, because without finding the destructor kfunc, we will bail and return an error. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20220424214901.2743946-7-memxor@gmail.com
This commit is contained in:
parent
4d7d7f69f4
commit
5ce937d613
2 changed files with 125 additions and 0 deletions
|
@ -40,6 +40,11 @@ struct btf_kfunc_id_set {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct btf_id_dtor_kfunc {
|
||||||
|
u32 btf_id;
|
||||||
|
u32 kfunc_btf_id;
|
||||||
|
};
|
||||||
|
|
||||||
extern const struct file_operations btf_fops;
|
extern const struct file_operations btf_fops;
|
||||||
|
|
||||||
void btf_get(struct btf *btf);
|
void btf_get(struct btf *btf);
|
||||||
|
@ -346,6 +351,9 @@ bool btf_kfunc_id_set_contains(const struct btf *btf,
|
||||||
enum btf_kfunc_type type, u32 kfunc_btf_id);
|
enum btf_kfunc_type type, u32 kfunc_btf_id);
|
||||||
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||||
const struct btf_kfunc_id_set *s);
|
const struct btf_kfunc_id_set *s);
|
||||||
|
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
|
||||||
|
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
|
||||||
|
struct module *owner);
|
||||||
#else
|
#else
|
||||||
static inline const struct btf_type *btf_type_by_id(const struct btf *btf,
|
static inline const struct btf_type *btf_type_by_id(const struct btf *btf,
|
||||||
u32 type_id)
|
u32 type_id)
|
||||||
|
@ -369,6 +377,15 @@ static inline int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
static inline s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id)
|
||||||
|
{
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
static inline int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors,
|
||||||
|
u32 add_cnt, struct module *owner)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
108
kernel/bpf/btf.c
108
kernel/bpf/btf.c
|
@ -207,12 +207,18 @@ enum btf_kfunc_hook {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BTF_KFUNC_SET_MAX_CNT = 32,
|
BTF_KFUNC_SET_MAX_CNT = 32,
|
||||||
|
BTF_DTOR_KFUNC_MAX_CNT = 256,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct btf_kfunc_set_tab {
|
struct btf_kfunc_set_tab {
|
||||||
struct btf_id_set *sets[BTF_KFUNC_HOOK_MAX][BTF_KFUNC_TYPE_MAX];
|
struct btf_id_set *sets[BTF_KFUNC_HOOK_MAX][BTF_KFUNC_TYPE_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct btf_id_dtor_kfunc_tab {
|
||||||
|
u32 cnt;
|
||||||
|
struct btf_id_dtor_kfunc dtors[];
|
||||||
|
};
|
||||||
|
|
||||||
struct btf {
|
struct btf {
|
||||||
void *data;
|
void *data;
|
||||||
struct btf_type **types;
|
struct btf_type **types;
|
||||||
|
@ -228,6 +234,7 @@ struct btf {
|
||||||
u32 id;
|
u32 id;
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
struct btf_kfunc_set_tab *kfunc_set_tab;
|
struct btf_kfunc_set_tab *kfunc_set_tab;
|
||||||
|
struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
|
||||||
|
|
||||||
/* split BTF support */
|
/* split BTF support */
|
||||||
struct btf *base_btf;
|
struct btf *base_btf;
|
||||||
|
@ -1616,8 +1623,19 @@ free_tab:
|
||||||
btf->kfunc_set_tab = NULL;
|
btf->kfunc_set_tab = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void btf_free_dtor_kfunc_tab(struct btf *btf)
|
||||||
|
{
|
||||||
|
struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab;
|
||||||
|
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
kfree(tab);
|
||||||
|
btf->dtor_kfunc_tab = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void btf_free(struct btf *btf)
|
static void btf_free(struct btf *btf)
|
||||||
{
|
{
|
||||||
|
btf_free_dtor_kfunc_tab(btf);
|
||||||
btf_free_kfunc_set_tab(btf);
|
btf_free_kfunc_set_tab(btf);
|
||||||
kvfree(btf->types);
|
kvfree(btf->types);
|
||||||
kvfree(btf->resolved_sizes);
|
kvfree(btf->resolved_sizes);
|
||||||
|
@ -7076,6 +7094,96 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set);
|
EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set);
|
||||||
|
|
||||||
|
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id)
|
||||||
|
{
|
||||||
|
struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab;
|
||||||
|
struct btf_id_dtor_kfunc *dtor;
|
||||||
|
|
||||||
|
if (!tab)
|
||||||
|
return -ENOENT;
|
||||||
|
/* Even though the size of tab->dtors[0] is > sizeof(u32), we only need
|
||||||
|
* to compare the first u32 with btf_id, so we can reuse btf_id_cmp_func.
|
||||||
|
*/
|
||||||
|
BUILD_BUG_ON(offsetof(struct btf_id_dtor_kfunc, btf_id) != 0);
|
||||||
|
dtor = bsearch(&btf_id, tab->dtors, tab->cnt, sizeof(tab->dtors[0]), btf_id_cmp_func);
|
||||||
|
if (!dtor)
|
||||||
|
return -ENOENT;
|
||||||
|
return dtor->kfunc_btf_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function must be invoked only from initcalls/module init functions */
|
||||||
|
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
|
||||||
|
struct module *owner)
|
||||||
|
{
|
||||||
|
struct btf_id_dtor_kfunc_tab *tab;
|
||||||
|
struct btf *btf;
|
||||||
|
u32 tab_cnt;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
btf = btf_get_module_btf(owner);
|
||||||
|
if (!btf) {
|
||||||
|
if (!owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
|
||||||
|
pr_err("missing vmlinux BTF, cannot register dtor kfuncs\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
if (owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) {
|
||||||
|
pr_err("missing module BTF, cannot register dtor kfuncs\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (IS_ERR(btf))
|
||||||
|
return PTR_ERR(btf);
|
||||||
|
|
||||||
|
if (add_cnt >= BTF_DTOR_KFUNC_MAX_CNT) {
|
||||||
|
pr_err("cannot register more than %d kfunc destructors\n", BTF_DTOR_KFUNC_MAX_CNT);
|
||||||
|
ret = -E2BIG;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab = btf->dtor_kfunc_tab;
|
||||||
|
/* Only one call allowed for modules */
|
||||||
|
if (WARN_ON_ONCE(tab && btf_is_module(btf))) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab_cnt = tab ? tab->cnt : 0;
|
||||||
|
if (tab_cnt > U32_MAX - add_cnt) {
|
||||||
|
ret = -EOVERFLOW;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (tab_cnt + add_cnt >= BTF_DTOR_KFUNC_MAX_CNT) {
|
||||||
|
pr_err("cannot register more than %d kfunc destructors\n", BTF_DTOR_KFUNC_MAX_CNT);
|
||||||
|
ret = -E2BIG;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab = krealloc(btf->dtor_kfunc_tab,
|
||||||
|
offsetof(struct btf_id_dtor_kfunc_tab, dtors[tab_cnt + add_cnt]),
|
||||||
|
GFP_KERNEL | __GFP_NOWARN);
|
||||||
|
if (!tab) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!btf->dtor_kfunc_tab)
|
||||||
|
tab->cnt = 0;
|
||||||
|
btf->dtor_kfunc_tab = tab;
|
||||||
|
|
||||||
|
memcpy(tab->dtors + tab->cnt, dtors, add_cnt * sizeof(tab->dtors[0]));
|
||||||
|
tab->cnt += add_cnt;
|
||||||
|
|
||||||
|
sort(tab->dtors, tab->cnt, sizeof(tab->dtors[0]), btf_id_cmp_func, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
end:
|
||||||
|
btf_free_dtor_kfunc_tab(btf);
|
||||||
|
btf_put(btf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(register_btf_id_dtor_kfuncs);
|
||||||
|
|
||||||
#define MAX_TYPES_ARE_COMPAT_DEPTH 2
|
#define MAX_TYPES_ARE_COMPAT_DEPTH 2
|
||||||
|
|
||||||
static
|
static
|
||||||
|
|
Loading…
Add table
Reference in a new issue