Skip to content

Commit 31af1aa

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf: Fixes for kprobe multi on kernel modules'
Jiri Olsa says: ==================== hi, Martynas reported kprobe _multi link does not resolve symbols from kernel modules, which attach by address works. In addition while fixing that I realized we do not take module reference if the module has kprobe_multi link on top of it and can be removed. There's mo crash related to this, it will silently disappear from ftrace tables, while kprobe_multi link stays up with no data. This patchset has fixes for both issues. v3 changes: - reorder fields in struct bpf_kprobe_multi_link [Andrii] - added ack [Andrii] v2 changes: - added acks (Song) - added comment to kallsyms_callback (Song) - change module_callback realloc logic (Andrii) - get rid of macros in tests (Andrii) thanks, jirka ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 152e60e + b244044 commit 31af1aa

File tree

11 files changed

+306
-17
lines changed

11 files changed

+306
-17
lines changed

include/linux/module.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,17 @@ static inline bool module_sig_ok(struct module *module)
879879
}
880880
#endif /* CONFIG_MODULE_SIG */
881881

882+
#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS)
882883
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
883884
struct module *, unsigned long),
884885
void *data);
886+
#else
887+
static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
888+
struct module *, unsigned long),
889+
void *data)
890+
{
891+
return -EOPNOTSUPP;
892+
}
893+
#endif /* CONFIG_MODULES && CONFIG_KALLSYMS */
885894

886895
#endif /* _LINUX_MODULE_H */

kernel/module/kallsyms.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,6 @@ unsigned long module_kallsyms_lookup_name(const char *name)
494494
return ret;
495495
}
496496

497-
#ifdef CONFIG_LIVEPATCH
498497
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
499498
struct module *, unsigned long),
500499
void *data)
@@ -531,4 +530,3 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
531530
mutex_unlock(&module_mutex);
532531
return ret;
533532
}
534-
#endif /* CONFIG_LIVEPATCH */

kernel/trace/bpf_trace.c

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,6 +2450,8 @@ struct bpf_kprobe_multi_link {
24502450
unsigned long *addrs;
24512451
u64 *cookies;
24522452
u32 cnt;
2453+
u32 mods_cnt;
2454+
struct module **mods;
24532455
};
24542456

24552457
struct bpf_kprobe_multi_run_ctx {
@@ -2505,6 +2507,14 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32
25052507
return err;
25062508
}
25072509

2510+
static void kprobe_multi_put_modules(struct module **mods, u32 cnt)
2511+
{
2512+
u32 i;
2513+
2514+
for (i = 0; i < cnt; i++)
2515+
module_put(mods[i]);
2516+
}
2517+
25082518
static void free_user_syms(struct user_syms *us)
25092519
{
25102520
kvfree(us->syms);
@@ -2517,6 +2527,7 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link)
25172527

25182528
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
25192529
unregister_fprobe(&kmulti_link->fp);
2530+
kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt);
25202531
}
25212532

25222533
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)
25262537
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
25272538
kvfree(kmulti_link->addrs);
25282539
kvfree(kmulti_link->cookies);
2540+
kfree(kmulti_link->mods);
25292541
kfree(kmulti_link);
25302542
}
25312543

@@ -2548,7 +2560,7 @@ static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void
25482560
swap(*cookie_a, *cookie_b);
25492561
}
25502562

2551-
static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b)
2563+
static int bpf_kprobe_multi_addrs_cmp(const void *a, const void *b)
25522564
{
25532565
const unsigned long *addr_a = a, *addr_b = b;
25542566

@@ -2559,7 +2571,7 @@ static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b)
25592571

25602572
static int bpf_kprobe_multi_cookie_cmp(const void *a, const void *b, const void *priv)
25612573
{
2562-
return __bpf_kprobe_multi_cookie_cmp(a, b);
2574+
return bpf_kprobe_multi_addrs_cmp(a, b);
25632575
}
25642576

25652577
static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx)
@@ -2577,7 +2589,7 @@ static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx)
25772589
return 0;
25782590
entry_ip = run_ctx->entry_ip;
25792591
addr = bsearch(&entry_ip, link->addrs, link->cnt, sizeof(entry_ip),
2580-
__bpf_kprobe_multi_cookie_cmp);
2592+
bpf_kprobe_multi_addrs_cmp);
25812593
if (!addr)
25822594
return 0;
25832595
cookie = link->cookies + (addr - link->addrs);
@@ -2661,6 +2673,71 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv)
26612673
}
26622674
}
26632675

2676+
struct module_addr_args {
2677+
unsigned long *addrs;
2678+
u32 addrs_cnt;
2679+
struct module **mods;
2680+
int mods_cnt;
2681+
int mods_cap;
2682+
};
2683+
2684+
static int module_callback(void *data, const char *name,
2685+
struct module *mod, unsigned long addr)
2686+
{
2687+
struct module_addr_args *args = data;
2688+
struct module **mods;
2689+
2690+
/* We iterate all modules symbols and for each we:
2691+
* - search for it in provided addresses array
2692+
* - if found we check if we already have the module pointer stored
2693+
* (we iterate modules sequentially, so we can check just the last
2694+
* module pointer)
2695+
* - take module reference and store it
2696+
*/
2697+
if (!bsearch(&addr, args->addrs, args->addrs_cnt, sizeof(addr),
2698+
bpf_kprobe_multi_addrs_cmp))
2699+
return 0;
2700+
2701+
if (args->mods && args->mods[args->mods_cnt - 1] == mod)
2702+
return 0;
2703+
2704+
if (args->mods_cnt == args->mods_cap) {
2705+
args->mods_cap = max(16, args->mods_cap * 3 / 2);
2706+
mods = krealloc_array(args->mods, args->mods_cap, sizeof(*mods), GFP_KERNEL);
2707+
if (!mods)
2708+
return -ENOMEM;
2709+
args->mods = mods;
2710+
}
2711+
2712+
if (!try_module_get(mod))
2713+
return -EINVAL;
2714+
2715+
args->mods[args->mods_cnt] = mod;
2716+
args->mods_cnt++;
2717+
return 0;
2718+
}
2719+
2720+
static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt)
2721+
{
2722+
struct module_addr_args args = {
2723+
.addrs = addrs,
2724+
.addrs_cnt = addrs_cnt,
2725+
};
2726+
int err;
2727+
2728+
/* We return either err < 0 in case of error, ... */
2729+
err = module_kallsyms_on_each_symbol(module_callback, &args);
2730+
if (err) {
2731+
kprobe_multi_put_modules(args.mods, args.mods_cnt);
2732+
kfree(args.mods);
2733+
return err;
2734+
}
2735+
2736+
/* or number of modules found if everything is ok. */
2737+
*mods = args.mods;
2738+
return args.mods_cnt;
2739+
}
2740+
26642741
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
26652742
{
26662743
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
27712848
bpf_kprobe_multi_cookie_cmp,
27722849
bpf_kprobe_multi_cookie_swap,
27732850
link);
2851+
} else {
2852+
/*
2853+
* We need to sort addrs array even if there are no cookies
2854+
* provided, to allow bsearch in get_modules_for_addrs.
2855+
*/
2856+
sort(addrs, cnt, sizeof(*addrs),
2857+
bpf_kprobe_multi_addrs_cmp, NULL);
2858+
}
2859+
2860+
err = get_modules_for_addrs(&link->mods, addrs, cnt);
2861+
if (err < 0) {
2862+
bpf_link_cleanup(&link_primer);
2863+
return err;
27742864
}
2865+
link->mods_cnt = err;
27752866

27762867
err = register_fprobe_ips(&link->fp, addrs, cnt);
27772868
if (err) {
2869+
kprobe_multi_put_modules(link->mods, link->mods_cnt);
27782870
bpf_link_cleanup(&link_primer);
27792871
return err;
27802872
}

kernel/trace/ftrace.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8267,6 +8267,10 @@ struct kallsyms_data {
82678267
size_t found;
82688268
};
82698269

8270+
/* This function gets called for all kernel and module symbols
8271+
* and returns 1 in case we resolved all the requested symbols,
8272+
* 0 otherwise.
8273+
*/
82708274
static int kallsyms_callback(void *data, const char *name,
82718275
struct module *mod, unsigned long addr)
82728276
{
@@ -8309,17 +8313,19 @@ static int kallsyms_callback(void *data, const char *name,
83098313
int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
83108314
{
83118315
struct kallsyms_data args;
8312-
int err;
8316+
int found_all;
83138317

83148318
memset(addrs, 0, sizeof(*addrs) * cnt);
83158319
args.addrs = addrs;
83168320
args.syms = sorted_syms;
83178321
args.cnt = cnt;
83188322
args.found = 0;
8319-
err = kallsyms_on_each_symbol(kallsyms_callback, &args);
8320-
if (err < 0)
8321-
return err;
8322-
return args.found == args.cnt ? 0 : -ESRCH;
8323+
8324+
found_all = kallsyms_on_each_symbol(kallsyms_callback, &args);
8325+
if (found_all)
8326+
return 0;
8327+
found_all = module_kallsyms_on_each_symbol(kallsyms_callback, &args);
8328+
return found_all ? 0 : -ESRCH;
83238329
}
83248330

83258331
#ifdef CONFIG_SYSCTL

tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,23 @@ __weak noinline struct file *bpf_testmod_return_ptr(int arg)
128128
}
129129
}
130130

131+
noinline int bpf_testmod_fentry_test1(int a)
132+
{
133+
return a + 1;
134+
}
135+
136+
noinline int bpf_testmod_fentry_test2(int a, u64 b)
137+
{
138+
return a + b;
139+
}
140+
141+
noinline int bpf_testmod_fentry_test3(char a, int b, u64 c)
142+
{
143+
return a + b + c;
144+
}
145+
146+
int bpf_testmod_fentry_ok;
147+
131148
noinline ssize_t
132149
bpf_testmod_test_read(struct file *file, struct kobject *kobj,
133150
struct bin_attribute *bin_attr,
@@ -167,6 +184,13 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
167184
return snprintf(buf, len, "%d\n", writable.val);
168185
}
169186

187+
if (bpf_testmod_fentry_test1(1) != 2 ||
188+
bpf_testmod_fentry_test2(2, 3) != 5 ||
189+
bpf_testmod_fentry_test3(4, 5, 6) != 15)
190+
goto out;
191+
192+
bpf_testmod_fentry_ok = 1;
193+
out:
170194
return -EIO; /* always fail */
171195
}
172196
EXPORT_SYMBOL(bpf_testmod_test_read);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <test_progs.h>
3+
#include "kprobe_multi.skel.h"
4+
#include "trace_helpers.h"
5+
#include "bpf/libbpf_internal.h"
6+
7+
static void kprobe_multi_testmod_check(struct kprobe_multi *skel)
8+
{
9+
ASSERT_EQ(skel->bss->kprobe_testmod_test1_result, 1, "kprobe_test1_result");
10+
ASSERT_EQ(skel->bss->kprobe_testmod_test2_result, 1, "kprobe_test2_result");
11+
ASSERT_EQ(skel->bss->kprobe_testmod_test3_result, 1, "kprobe_test3_result");
12+
13+
ASSERT_EQ(skel->bss->kretprobe_testmod_test1_result, 1, "kretprobe_test1_result");
14+
ASSERT_EQ(skel->bss->kretprobe_testmod_test2_result, 1, "kretprobe_test2_result");
15+
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");
16+
}
17+
18+
static void test_testmod_attach_api(struct bpf_kprobe_multi_opts *opts)
19+
{
20+
struct kprobe_multi *skel = NULL;
21+
22+
skel = kprobe_multi__open_and_load();
23+
if (!ASSERT_OK_PTR(skel, "fentry_raw_skel_load"))
24+
return;
25+
26+
skel->bss->pid = getpid();
27+
28+
skel->links.test_kprobe_testmod = bpf_program__attach_kprobe_multi_opts(
29+
skel->progs.test_kprobe_testmod,
30+
NULL, opts);
31+
if (!skel->links.test_kprobe_testmod)
32+
goto cleanup;
33+
34+
opts->retprobe = true;
35+
skel->links.test_kretprobe_testmod = bpf_program__attach_kprobe_multi_opts(
36+
skel->progs.test_kretprobe_testmod,
37+
NULL, opts);
38+
if (!skel->links.test_kretprobe_testmod)
39+
goto cleanup;
40+
41+
ASSERT_OK(trigger_module_test_read(1), "trigger_read");
42+
kprobe_multi_testmod_check(skel);
43+
44+
cleanup:
45+
kprobe_multi__destroy(skel);
46+
}
47+
48+
static void test_testmod_attach_api_addrs(void)
49+
{
50+
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
51+
unsigned long long addrs[3];
52+
53+
addrs[0] = ksym_get_addr("bpf_testmod_fentry_test1");
54+
ASSERT_NEQ(addrs[0], 0, "ksym_get_addr");
55+
addrs[1] = ksym_get_addr("bpf_testmod_fentry_test2");
56+
ASSERT_NEQ(addrs[1], 0, "ksym_get_addr");
57+
addrs[2] = ksym_get_addr("bpf_testmod_fentry_test3");
58+
ASSERT_NEQ(addrs[2], 0, "ksym_get_addr");
59+
60+
opts.addrs = (const unsigned long *) addrs;
61+
opts.cnt = ARRAY_SIZE(addrs);
62+
63+
test_testmod_attach_api(&opts);
64+
}
65+
66+
static void test_testmod_attach_api_syms(void)
67+
{
68+
LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
69+
const char *syms[3] = {
70+
"bpf_testmod_fentry_test1",
71+
"bpf_testmod_fentry_test2",
72+
"bpf_testmod_fentry_test3",
73+
};
74+
75+
opts.syms = syms;
76+
opts.cnt = ARRAY_SIZE(syms);
77+
test_testmod_attach_api(&opts);
78+
}
79+
80+
void serial_test_kprobe_multi_testmod_test(void)
81+
{
82+
if (!ASSERT_OK(load_kallsyms_refresh(), "load_kallsyms_refresh"))
83+
return;
84+
85+
if (test__start_subtest("testmod_attach_api_syms"))
86+
test_testmod_attach_api_syms();
87+
if (test__start_subtest("testmod_attach_api_addrs"))
88+
test_testmod_attach_api_addrs();
89+
}

tools/testing/selftests/bpf/prog_tests/module_attach.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ void test_module_attach(void)
103103
ASSERT_ERR(delete_module("bpf_testmod", 0), "delete_module");
104104
bpf_link__destroy(link);
105105

106+
link = bpf_program__attach(skel->progs.kprobe_multi);
107+
if (!ASSERT_OK_PTR(link, "attach_kprobe_multi"))
108+
goto cleanup;
109+
110+
ASSERT_ERR(delete_module("bpf_testmod", 0), "delete_module");
111+
bpf_link__destroy(link);
112+
106113
cleanup:
107114
test_module_attach__destroy(skel);
108115
}

0 commit comments

Comments
 (0)