Skip to content

Commit 36e6844

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: Load and verify kernel module BTFs
Add kernel module listener that will load/validate and unload module BTF. Module BTFs gets ID generated for them, which makes it possible to iterate them with existing BTF iteration API. They are given their respective module's names, which will get reported through GET_OBJ_INFO API. They are also marked as in-kernel BTFs for tooling to distinguish them from user-provided BTFs. Also, similarly to vmlinux BTF, kernel module BTFs are exposed through sysfs as /sys/kernel/btf/<module-name>. This is convenient for user-space tools to inspect module BTF contents and dump their types with existing tools: [vmuser@archvm bpf]$ ls -la /sys/kernel/btf total 0 drwxr-xr-x 2 root root 0 Nov 4 19:46 . drwxr-xr-x 13 root root 0 Nov 4 19:46 .. ... -r--r--r-- 1 root root 888 Nov 4 19:46 irqbypass -r--r--r-- 1 root root 100225 Nov 4 19:46 kvm -r--r--r-- 1 root root 35401 Nov 4 19:46 kvm_intel -r--r--r-- 1 root root 120 Nov 4 19:46 pcspkr -r--r--r-- 1 root root 399 Nov 4 19:46 serio_raw -r--r--r-- 1 root root 4094095 Nov 4 19:46 vmlinux Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 5f9ae91 commit 36e6844

File tree

6 files changed

+241
-1
lines changed

6 files changed

+241
-1
lines changed

Documentation/ABI/testing/sysfs-kernel-btf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ Description:
1515
information with description of all internal kernel types. See
1616
Documentation/bpf/btf.rst for detailed description of format
1717
itself.
18+
19+
What: /sys/kernel/btf/<module-name>
20+
Date: Nov 2020
21+
KernelVersion: 5.11
22+
23+
Description:
24+
Read-only binary attribute exposing kernel module's BTF type
25+
information as an add-on to the kernel's BTF (/sys/kernel/btf/vmlinux).

include/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ struct seq_operations;
3636
struct bpf_iter_aux_info;
3737
struct bpf_local_storage;
3838
struct bpf_local_storage_map;
39+
struct kobject;
3940

4041
extern struct idr btf_idr;
4142
extern spinlock_t btf_idr_lock;
43+
extern struct kobject *btf_kobj;
4244

4345
typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
4446
struct bpf_iter_aux_info *aux);

include/linux/module.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ struct module {
475475
unsigned int num_bpf_raw_events;
476476
struct bpf_raw_event_map *bpf_raw_events;
477477
#endif
478+
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
479+
unsigned int btf_data_size;
480+
void *btf_data;
481+
#endif
478482
#ifdef CONFIG_JUMP_LABEL
479483
struct jump_entry *jump_entries;
480484
unsigned int num_jump_entries;

kernel/bpf/btf.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <linux/skmsg.h>
2323
#include <linux/perf_event.h>
2424
#include <linux/bsearch.h>
25+
#include <linux/kobject.h>
26+
#include <linux/sysfs.h>
2527
#include <net/sock.h>
2628

2729
/* BTF (BPF Type Format) is the meta data format which describes
@@ -4476,6 +4478,75 @@ struct btf *btf_parse_vmlinux(void)
44764478
return ERR_PTR(err);
44774479
}
44784480

4481+
static struct btf *btf_parse_module(const char *module_name, const void *data, unsigned int data_size)
4482+
{
4483+
struct btf_verifier_env *env = NULL;
4484+
struct bpf_verifier_log *log;
4485+
struct btf *btf = NULL, *base_btf;
4486+
int err;
4487+
4488+
base_btf = bpf_get_btf_vmlinux();
4489+
if (IS_ERR(base_btf))
4490+
return base_btf;
4491+
if (!base_btf)
4492+
return ERR_PTR(-EINVAL);
4493+
4494+
env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN);
4495+
if (!env)
4496+
return ERR_PTR(-ENOMEM);
4497+
4498+
log = &env->log;
4499+
log->level = BPF_LOG_KERNEL;
4500+
4501+
btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN);
4502+
if (!btf) {
4503+
err = -ENOMEM;
4504+
goto errout;
4505+
}
4506+
env->btf = btf;
4507+
4508+
btf->base_btf = base_btf;
4509+
btf->start_id = base_btf->nr_types;
4510+
btf->start_str_off = base_btf->hdr.str_len;
4511+
btf->kernel_btf = true;
4512+
snprintf(btf->name, sizeof(btf->name), "%s", module_name);
4513+
4514+
btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN);
4515+
if (!btf->data) {
4516+
err = -ENOMEM;
4517+
goto errout;
4518+
}
4519+
memcpy(btf->data, data, data_size);
4520+
btf->data_size = data_size;
4521+
4522+
err = btf_parse_hdr(env);
4523+
if (err)
4524+
goto errout;
4525+
4526+
btf->nohdr_data = btf->data + btf->hdr.hdr_len;
4527+
4528+
err = btf_parse_str_sec(env);
4529+
if (err)
4530+
goto errout;
4531+
4532+
err = btf_check_all_metas(env);
4533+
if (err)
4534+
goto errout;
4535+
4536+
btf_verifier_env_free(env);
4537+
refcount_set(&btf->refcnt, 1);
4538+
return btf;
4539+
4540+
errout:
4541+
btf_verifier_env_free(env);
4542+
if (btf) {
4543+
kvfree(btf->data);
4544+
kvfree(btf->types);
4545+
kfree(btf);
4546+
}
4547+
return ERR_PTR(err);
4548+
}
4549+
44794550
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog)
44804551
{
44814552
struct bpf_prog *tgt_prog = prog->aux->dst_prog;
@@ -5651,3 +5722,126 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id)
56515722
{
56525723
return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL;
56535724
}
5725+
5726+
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
5727+
struct btf_module {
5728+
struct list_head list;
5729+
struct module *module;
5730+
struct btf *btf;
5731+
struct bin_attribute *sysfs_attr;
5732+
};
5733+
5734+
static LIST_HEAD(btf_modules);
5735+
static DEFINE_MUTEX(btf_module_mutex);
5736+
5737+
static ssize_t
5738+
btf_module_read(struct file *file, struct kobject *kobj,
5739+
struct bin_attribute *bin_attr,
5740+
char *buf, loff_t off, size_t len)
5741+
{
5742+
const struct btf *btf = bin_attr->private;
5743+
5744+
memcpy(buf, btf->data + off, len);
5745+
return len;
5746+
}
5747+
5748+
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
5749+
void *module)
5750+
{
5751+
struct btf_module *btf_mod, *tmp;
5752+
struct module *mod = module;
5753+
struct btf *btf;
5754+
int err = 0;
5755+
5756+
if (mod->btf_data_size == 0 ||
5757+
(op != MODULE_STATE_COMING && op != MODULE_STATE_GOING))
5758+
goto out;
5759+
5760+
switch (op) {
5761+
case MODULE_STATE_COMING:
5762+
btf_mod = kzalloc(sizeof(*btf_mod), GFP_KERNEL);
5763+
if (!btf_mod) {
5764+
err = -ENOMEM;
5765+
goto out;
5766+
}
5767+
btf = btf_parse_module(mod->name, mod->btf_data, mod->btf_data_size);
5768+
if (IS_ERR(btf)) {
5769+
pr_warn("failed to validate module [%s] BTF: %ld\n",
5770+
mod->name, PTR_ERR(btf));
5771+
kfree(btf_mod);
5772+
err = PTR_ERR(btf);
5773+
goto out;
5774+
}
5775+
err = btf_alloc_id(btf);
5776+
if (err) {
5777+
btf_free(btf);
5778+
kfree(btf_mod);
5779+
goto out;
5780+
}
5781+
5782+
mutex_lock(&btf_module_mutex);
5783+
btf_mod->module = module;
5784+
btf_mod->btf = btf;
5785+
list_add(&btf_mod->list, &btf_modules);
5786+
mutex_unlock(&btf_module_mutex);
5787+
5788+
if (IS_ENABLED(CONFIG_SYSFS)) {
5789+
struct bin_attribute *attr;
5790+
5791+
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
5792+
if (!attr)
5793+
goto out;
5794+
5795+
sysfs_bin_attr_init(attr);
5796+
attr->attr.name = btf->name;
5797+
attr->attr.mode = 0444;
5798+
attr->size = btf->data_size;
5799+
attr->private = btf;
5800+
attr->read = btf_module_read;
5801+
5802+
err = sysfs_create_bin_file(btf_kobj, attr);
5803+
if (err) {
5804+
pr_warn("failed to register module [%s] BTF in sysfs: %d\n",
5805+
mod->name, err);
5806+
kfree(attr);
5807+
err = 0;
5808+
goto out;
5809+
}
5810+
5811+
btf_mod->sysfs_attr = attr;
5812+
}
5813+
5814+
break;
5815+
case MODULE_STATE_GOING:
5816+
mutex_lock(&btf_module_mutex);
5817+
list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
5818+
if (btf_mod->module != module)
5819+
continue;
5820+
5821+
list_del(&btf_mod->list);
5822+
if (btf_mod->sysfs_attr)
5823+
sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr);
5824+
btf_put(btf_mod->btf);
5825+
kfree(btf_mod->sysfs_attr);
5826+
kfree(btf_mod);
5827+
break;
5828+
}
5829+
mutex_unlock(&btf_module_mutex);
5830+
break;
5831+
}
5832+
out:
5833+
return notifier_from_errno(err);
5834+
}
5835+
5836+
static struct notifier_block btf_module_nb = {
5837+
.notifier_call = btf_module_notify,
5838+
};
5839+
5840+
static int __init btf_module_init(void)
5841+
{
5842+
register_module_notifier(&btf_module_nb);
5843+
return 0;
5844+
}
5845+
5846+
fs_initcall(btf_module_init);
5847+
#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */

kernel/bpf/sysfs_btf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
2626
.read = btf_vmlinux_read,
2727
};
2828

29-
static struct kobject *btf_kobj;
29+
struct kobject *btf_kobj;
3030

3131
static int __init btf_vmlinux_init(void)
3232
{

kernel/module.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,35 @@ static void *section_objs(const struct load_info *info,
380380
return (void *)info->sechdrs[sec].sh_addr;
381381
}
382382

383+
/* Find a module section: 0 means not found. Ignores SHF_ALLOC flag. */
384+
static unsigned int find_any_sec(const struct load_info *info, const char *name)
385+
{
386+
unsigned int i;
387+
388+
for (i = 1; i < info->hdr->e_shnum; i++) {
389+
Elf_Shdr *shdr = &info->sechdrs[i];
390+
if (strcmp(info->secstrings + shdr->sh_name, name) == 0)
391+
return i;
392+
}
393+
return 0;
394+
}
395+
396+
/*
397+
* Find a module section, or NULL. Fill in number of "objects" in section.
398+
* Ignores SHF_ALLOC flag.
399+
*/
400+
static __maybe_unused void *any_section_objs(const struct load_info *info,
401+
const char *name,
402+
size_t object_size,
403+
unsigned int *num)
404+
{
405+
unsigned int sec = find_any_sec(info, name);
406+
407+
/* Section 0 has sh_addr 0 and sh_size 0. */
408+
*num = info->sechdrs[sec].sh_size / object_size;
409+
return (void *)info->sechdrs[sec].sh_addr;
410+
}
411+
383412
/* Provided by the linker */
384413
extern const struct kernel_symbol __start___ksymtab[];
385414
extern const struct kernel_symbol __stop___ksymtab[];
@@ -3250,6 +3279,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
32503279
sizeof(*mod->bpf_raw_events),
32513280
&mod->num_bpf_raw_events);
32523281
#endif
3282+
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
3283+
mod->btf_data = any_section_objs(info, ".BTF", 1, &mod->btf_data_size);
3284+
#endif
32533285
#ifdef CONFIG_JUMP_LABEL
32543286
mod->jump_entries = section_objs(info, "__jump_table",
32553287
sizeof(*mod->jump_entries),

0 commit comments

Comments
 (0)