Skip to content

Commit

Permalink
bpf: Take module reference on kprobe_multi link
Browse files Browse the repository at this point in the history
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: Andrii Nakryiko <[email protected]>
Signed-off-by: Jiri Olsa <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Alexei Starovoitov <[email protected]>
  • Loading branch information
olsajiri authored and Alexei Starovoitov committed Oct 25, 2022
1 parent 1a1b071 commit e22061b
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions kernel/trace/bpf_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2450,6 +2450,8 @@ struct bpf_kprobe_multi_link {
unsigned long *addrs;
u64 *cookies;
u32 cnt;
u32 mods_cnt;
struct module **mods;
};

struct bpf_kprobe_multi_run_ctx {
Expand Down Expand Up @@ -2505,6 +2507,14 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32
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)
{
kvfree(us->syms);
Expand All @@ -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);
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)
Expand All @@ -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);
kvfree(kmulti_link->addrs);
kvfree(kmulti_link->cookies);
kfree(kmulti_link->mods);
kfree(kmulti_link);
}

Expand Down Expand Up @@ -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)
{
struct bpf_kprobe_multi_link *link = NULL;
Expand Down Expand Up @@ -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_swap,
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);
if (err) {
kprobe_multi_put_modules(link->mods, link->mods_cnt);
bpf_link_cleanup(&link_primer);
return err;
}
Expand Down

0 comments on commit e22061b

Please sign in to comment.