Commit e22061b2 authored by Jiri Olsa's avatar Jiri Olsa Committed by Alexei Starovoitov

bpf: Take module reference on kprobe_multi link

Currently we allow to create kprobe multi link on function from kernel
module, but we don't take the module reference to ensure it's not
unloaded while we are tracing it.

The multi kprobe link is based on fprobe/ftrace layer which takes
different approach and releases ftrace hooks when module is unloaded
even if there's tracer registered on top of it.

Adding code that gathers all the related modules for the link and takes
their references before it's attached. All kernel module references are
released after link is unregistered.

Note that we do it the same way already for trampoline probes
(but for single address).
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20221025134148.3300700-5-jolsa@kernel.orgSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 1a1b0716
...@@ -2450,6 +2450,8 @@ struct bpf_kprobe_multi_link { ...@@ -2450,6 +2450,8 @@ struct bpf_kprobe_multi_link {
unsigned long *addrs; unsigned long *addrs;
u64 *cookies; u64 *cookies;
u32 cnt; u32 cnt;
u32 mods_cnt;
struct module **mods;
}; };
struct bpf_kprobe_multi_run_ctx { struct bpf_kprobe_multi_run_ctx {
...@@ -2505,6 +2507,14 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 ...@@ -2505,6 +2507,14 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32
return err; return err;
} }
static void kprobe_multi_put_modules(struct module **mods, u32 cnt)
{
u32 i;
for (i = 0; i < cnt; i++)
module_put(mods[i]);
}
static void free_user_syms(struct user_syms *us) static void free_user_syms(struct user_syms *us)
{ {
kvfree(us->syms); kvfree(us->syms);
...@@ -2517,6 +2527,7 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) ...@@ -2517,6 +2527,7 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link)
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
unregister_fprobe(&kmulti_link->fp); unregister_fprobe(&kmulti_link->fp);
kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt);
} }
static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link)
...@@ -2526,6 +2537,7 @@ static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) ...@@ -2526,6 +2537,7 @@ static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link)
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
kvfree(kmulti_link->addrs); kvfree(kmulti_link->addrs);
kvfree(kmulti_link->cookies); kvfree(kmulti_link->cookies);
kfree(kmulti_link->mods);
kfree(kmulti_link); kfree(kmulti_link);
} }
...@@ -2661,6 +2673,71 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv) ...@@ -2661,6 +2673,71 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv)
} }
} }
struct module_addr_args {
unsigned long *addrs;
u32 addrs_cnt;
struct module **mods;
int mods_cnt;
int mods_cap;
};
static int module_callback(void *data, const char *name,
struct module *mod, unsigned long addr)
{
struct module_addr_args *args = data;
struct module **mods;
/* We iterate all modules symbols and for each we:
* - search for it in provided addresses array
* - if found we check if we already have the module pointer stored
* (we iterate modules sequentially, so we can check just the last
* module pointer)
* - take module reference and store it
*/
if (!bsearch(&addr, args->addrs, args->addrs_cnt, sizeof(addr),
bpf_kprobe_multi_addrs_cmp))
return 0;
if (args->mods && args->mods[args->mods_cnt - 1] == mod)
return 0;
if (args->mods_cnt == args->mods_cap) {
args->mods_cap = max(16, args->mods_cap * 3 / 2);
mods = krealloc_array(args->mods, args->mods_cap, sizeof(*mods), GFP_KERNEL);
if (!mods)
return -ENOMEM;
args->mods = mods;
}
if (!try_module_get(mod))
return -EINVAL;
args->mods[args->mods_cnt] = mod;
args->mods_cnt++;
return 0;
}
static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt)
{
struct module_addr_args args = {
.addrs = addrs,
.addrs_cnt = addrs_cnt,
};
int err;
/* We return either err < 0 in case of error, ... */
err = module_kallsyms_on_each_symbol(module_callback, &args);
if (err) {
kprobe_multi_put_modules(args.mods, args.mods_cnt);
kfree(args.mods);
return err;
}
/* or number of modules found if everything is ok. */
*mods = args.mods;
return args.mods_cnt;
}
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{ {
struct bpf_kprobe_multi_link *link = NULL; struct bpf_kprobe_multi_link *link = NULL;
...@@ -2771,10 +2848,25 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr ...@@ -2771,10 +2848,25 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
bpf_kprobe_multi_cookie_cmp, bpf_kprobe_multi_cookie_cmp,
bpf_kprobe_multi_cookie_swap, bpf_kprobe_multi_cookie_swap,
link); link);
} else {
/*
* We need to sort addrs array even if there are no cookies
* provided, to allow bsearch in get_modules_for_addrs.
*/
sort(addrs, cnt, sizeof(*addrs),
bpf_kprobe_multi_addrs_cmp, NULL);
}
err = get_modules_for_addrs(&link->mods, addrs, cnt);
if (err < 0) {
bpf_link_cleanup(&link_primer);
return err;
} }
link->mods_cnt = err;
err = register_fprobe_ips(&link->fp, addrs, cnt); err = register_fprobe_ips(&link->fp, addrs, cnt);
if (err) { if (err) {
kprobe_multi_put_modules(link->mods, link->mods_cnt);
bpf_link_cleanup(&link_primer); bpf_link_cleanup(&link_primer);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment