Skip to content

Commit 0dcac27

Browse files
olsajiriAlexei Starovoitov
authored andcommitted
bpf: Add multi kprobe link
Adding new link type BPF_LINK_TYPE_KPROBE_MULTI that attaches kprobe program through fprobe API. The fprobe API allows to attach probe on multiple functions at once very fast, because it works on top of ftrace. On the other hand this limits the probe point to the function entry or return. The kprobe program gets the same pt_regs input ctx as when it's attached through the perf API. Adding new attach type BPF_TRACE_KPROBE_MULTI that allows attachment kprobe to multiple function with new link. User provides array of addresses or symbols with count to attach the kprobe program to. The new link_create uapi interface looks like: struct { __u32 flags; __u32 cnt; __aligned_u64 syms; __aligned_u64 addrs; } kprobe_multi; The flags field allows single BPF_TRACE_KPROBE_MULTI bit to create return multi kprobe. Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Jiri Olsa <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent aecf489 commit 0dcac27

File tree

6 files changed

+266
-5
lines changed

6 files changed

+266
-5
lines changed

include/linux/bpf_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,4 @@ BPF_LINK_TYPE(BPF_LINK_TYPE_XDP, xdp)
140140
#ifdef CONFIG_PERF_EVENTS
141141
BPF_LINK_TYPE(BPF_LINK_TYPE_PERF_EVENT, perf)
142142
#endif
143+
BPF_LINK_TYPE(BPF_LINK_TYPE_KPROBE_MULTI, kprobe_multi)

include/linux/trace_events.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct array_buffer;
1515
struct tracer;
1616
struct dentry;
1717
struct bpf_prog;
18+
union bpf_attr;
1819

1920
const char *trace_print_flags_seq(struct trace_seq *p, const char *delim,
2021
unsigned long flags,
@@ -738,6 +739,7 @@ void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp);
738739
int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
739740
u32 *fd_type, const char **buf,
740741
u64 *probe_offset, u64 *probe_addr);
742+
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
741743
#else
742744
static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
743745
{
@@ -779,6 +781,11 @@ static inline int bpf_get_perf_event_info(const struct perf_event *event,
779781
{
780782
return -EOPNOTSUPP;
781783
}
784+
static inline int
785+
bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
786+
{
787+
return -EOPNOTSUPP;
788+
}
782789
#endif
783790

784791
enum {

include/uapi/linux/bpf.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ enum bpf_attach_type {
997997
BPF_SK_REUSEPORT_SELECT,
998998
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
999999
BPF_PERF_EVENT,
1000+
BPF_TRACE_KPROBE_MULTI,
10001001
__MAX_BPF_ATTACH_TYPE
10011002
};
10021003

@@ -1011,6 +1012,7 @@ enum bpf_link_type {
10111012
BPF_LINK_TYPE_NETNS = 5,
10121013
BPF_LINK_TYPE_XDP = 6,
10131014
BPF_LINK_TYPE_PERF_EVENT = 7,
1015+
BPF_LINK_TYPE_KPROBE_MULTI = 8,
10141016

10151017
MAX_BPF_LINK_TYPE,
10161018
};
@@ -1118,6 +1120,11 @@ enum bpf_link_type {
11181120
*/
11191121
#define BPF_F_XDP_HAS_FRAGS (1U << 5)
11201122

1123+
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
1124+
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
1125+
*/
1126+
#define BPF_F_KPROBE_MULTI_RETURN (1U << 0)
1127+
11211128
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
11221129
* the following extensions:
11231130
*
@@ -1475,6 +1482,12 @@ union bpf_attr {
14751482
*/
14761483
__u64 bpf_cookie;
14771484
} perf_event;
1485+
struct {
1486+
__u32 flags;
1487+
__u32 cnt;
1488+
__aligned_u64 syms;
1489+
__aligned_u64 addrs;
1490+
} kprobe_multi;
14781491
};
14791492
} link_create;
14801493

kernel/bpf/syscall.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <linux/bpf-netns.h>
3333
#include <linux/rcupdate_trace.h>
3434
#include <linux/memcontrol.h>
35+
#include <linux/trace_events.h>
3536

3637
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
3738
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
@@ -3022,6 +3023,11 @@ static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *pro
30223023
fput(perf_file);
30233024
return err;
30243025
}
3026+
#else
3027+
static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
3028+
{
3029+
return -EOPNOTSUPP;
3030+
}
30253031
#endif /* CONFIG_PERF_EVENTS */
30263032

30273033
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
@@ -4255,7 +4261,7 @@ static int tracing_bpf_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
42554261
return -EINVAL;
42564262
}
42574263

4258-
#define BPF_LINK_CREATE_LAST_FIELD link_create.iter_info_len
4264+
#define BPF_LINK_CREATE_LAST_FIELD link_create.kprobe_multi.addrs
42594265
static int link_create(union bpf_attr *attr, bpfptr_t uattr)
42604266
{
42614267
enum bpf_prog_type ptype;
@@ -4279,14 +4285,21 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
42794285
ret = tracing_bpf_link_attach(attr, uattr, prog);
42804286
goto out;
42814287
case BPF_PROG_TYPE_PERF_EVENT:
4282-
case BPF_PROG_TYPE_KPROBE:
42834288
case BPF_PROG_TYPE_TRACEPOINT:
42844289
if (attr->link_create.attach_type != BPF_PERF_EVENT) {
42854290
ret = -EINVAL;
42864291
goto out;
42874292
}
42884293
ptype = prog->type;
42894294
break;
4295+
case BPF_PROG_TYPE_KPROBE:
4296+
if (attr->link_create.attach_type != BPF_PERF_EVENT &&
4297+
attr->link_create.attach_type != BPF_TRACE_KPROBE_MULTI) {
4298+
ret = -EINVAL;
4299+
goto out;
4300+
}
4301+
ptype = prog->type;
4302+
break;
42904303
default:
42914304
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
42924305
if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) {
@@ -4318,13 +4331,16 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
43184331
ret = bpf_xdp_link_attach(attr, prog);
43194332
break;
43204333
#endif
4321-
#ifdef CONFIG_PERF_EVENTS
43224334
case BPF_PROG_TYPE_PERF_EVENT:
43234335
case BPF_PROG_TYPE_TRACEPOINT:
4324-
case BPF_PROG_TYPE_KPROBE:
43254336
ret = bpf_perf_link_attach(attr, prog);
43264337
break;
4327-
#endif
4338+
case BPF_PROG_TYPE_KPROBE:
4339+
if (attr->link_create.attach_type == BPF_PERF_EVENT)
4340+
ret = bpf_perf_link_attach(attr, prog);
4341+
else
4342+
ret = bpf_kprobe_multi_link_attach(attr, prog);
4343+
break;
43284344
default:
43294345
ret = -EINVAL;
43304346
}

kernel/trace/bpf_trace.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/error-injection.h>
1818
#include <linux/btf_ids.h>
1919
#include <linux/bpf_lsm.h>
20+
#include <linux/fprobe.h>
2021

2122
#include <net/bpf_sk_storage.h>
2223

@@ -2181,3 +2182,213 @@ static int __init bpf_event_init(void)
21812182

21822183
fs_initcall(bpf_event_init);
21832184
#endif /* CONFIG_MODULES */
2185+
2186+
#ifdef CONFIG_FPROBE
2187+
struct bpf_kprobe_multi_link {
2188+
struct bpf_link link;
2189+
struct fprobe fp;
2190+
unsigned long *addrs;
2191+
};
2192+
2193+
static void bpf_kprobe_multi_link_release(struct bpf_link *link)
2194+
{
2195+
struct bpf_kprobe_multi_link *kmulti_link;
2196+
2197+
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
2198+
unregister_fprobe(&kmulti_link->fp);
2199+
}
2200+
2201+
static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link)
2202+
{
2203+
struct bpf_kprobe_multi_link *kmulti_link;
2204+
2205+
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
2206+
kvfree(kmulti_link->addrs);
2207+
kfree(kmulti_link);
2208+
}
2209+
2210+
static const struct bpf_link_ops bpf_kprobe_multi_link_lops = {
2211+
.release = bpf_kprobe_multi_link_release,
2212+
.dealloc = bpf_kprobe_multi_link_dealloc,
2213+
};
2214+
2215+
static int
2216+
kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
2217+
struct pt_regs *regs)
2218+
{
2219+
int err;
2220+
2221+
if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) {
2222+
err = 0;
2223+
goto out;
2224+
}
2225+
2226+
migrate_disable();
2227+
rcu_read_lock();
2228+
err = bpf_prog_run(link->link.prog, regs);
2229+
rcu_read_unlock();
2230+
migrate_enable();
2231+
2232+
out:
2233+
__this_cpu_dec(bpf_prog_active);
2234+
return err;
2235+
}
2236+
2237+
static void
2238+
kprobe_multi_link_handler(struct fprobe *fp, unsigned long entry_ip,
2239+
struct pt_regs *regs)
2240+
{
2241+
unsigned long saved_ip = instruction_pointer(regs);
2242+
struct bpf_kprobe_multi_link *link;
2243+
2244+
/*
2245+
* Because fprobe's regs->ip is set to the next instruction of
2246+
* dynamic-ftrace instruction, correct entry ip must be set, so
2247+
* that the bpf program can access entry address via regs as same
2248+
* as kprobes.
2249+
*
2250+
* Both kprobe and kretprobe see the entry ip of traced function
2251+
* as instruction pointer.
2252+
*/
2253+
instruction_pointer_set(regs, entry_ip);
2254+
2255+
link = container_of(fp, struct bpf_kprobe_multi_link, fp);
2256+
kprobe_multi_link_prog_run(link, regs);
2257+
2258+
instruction_pointer_set(regs, saved_ip);
2259+
}
2260+
2261+
static int
2262+
kprobe_multi_resolve_syms(const void *usyms, u32 cnt,
2263+
unsigned long *addrs)
2264+
{
2265+
unsigned long addr, size;
2266+
const char **syms;
2267+
int err = -ENOMEM;
2268+
unsigned int i;
2269+
char *func;
2270+
2271+
size = cnt * sizeof(*syms);
2272+
syms = kvzalloc(size, GFP_KERNEL);
2273+
if (!syms)
2274+
return -ENOMEM;
2275+
2276+
func = kmalloc(KSYM_NAME_LEN, GFP_KERNEL);
2277+
if (!func)
2278+
goto error;
2279+
2280+
if (copy_from_user(syms, usyms, size)) {
2281+
err = -EFAULT;
2282+
goto error;
2283+
}
2284+
2285+
for (i = 0; i < cnt; i++) {
2286+
err = strncpy_from_user(func, syms[i], KSYM_NAME_LEN);
2287+
if (err == KSYM_NAME_LEN)
2288+
err = -E2BIG;
2289+
if (err < 0)
2290+
goto error;
2291+
err = -EINVAL;
2292+
addr = kallsyms_lookup_name(func);
2293+
if (!addr)
2294+
goto error;
2295+
if (!kallsyms_lookup_size_offset(addr, &size, NULL))
2296+
goto error;
2297+
addr = ftrace_location_range(addr, addr + size - 1);
2298+
if (!addr)
2299+
goto error;
2300+
addrs[i] = addr;
2301+
}
2302+
2303+
err = 0;
2304+
error:
2305+
kvfree(syms);
2306+
kfree(func);
2307+
return err;
2308+
}
2309+
2310+
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
2311+
{
2312+
struct bpf_kprobe_multi_link *link = NULL;
2313+
struct bpf_link_primer link_primer;
2314+
unsigned long *addrs;
2315+
u32 flags, cnt, size;
2316+
void __user *uaddrs;
2317+
void __user *usyms;
2318+
int err;
2319+
2320+
/* no support for 32bit archs yet */
2321+
if (sizeof(u64) != sizeof(void *))
2322+
return -EOPNOTSUPP;
2323+
2324+
if (prog->expected_attach_type != BPF_TRACE_KPROBE_MULTI)
2325+
return -EINVAL;
2326+
2327+
flags = attr->link_create.kprobe_multi.flags;
2328+
if (flags & ~BPF_F_KPROBE_MULTI_RETURN)
2329+
return -EINVAL;
2330+
2331+
uaddrs = u64_to_user_ptr(attr->link_create.kprobe_multi.addrs);
2332+
usyms = u64_to_user_ptr(attr->link_create.kprobe_multi.syms);
2333+
if (!!uaddrs == !!usyms)
2334+
return -EINVAL;
2335+
2336+
cnt = attr->link_create.kprobe_multi.cnt;
2337+
if (!cnt)
2338+
return -EINVAL;
2339+
2340+
size = cnt * sizeof(*addrs);
2341+
addrs = kvmalloc(size, GFP_KERNEL);
2342+
if (!addrs)
2343+
return -ENOMEM;
2344+
2345+
if (uaddrs) {
2346+
if (copy_from_user(addrs, uaddrs, size)) {
2347+
err = -EFAULT;
2348+
goto error;
2349+
}
2350+
} else {
2351+
err = kprobe_multi_resolve_syms(usyms, cnt, addrs);
2352+
if (err)
2353+
goto error;
2354+
}
2355+
2356+
link = kzalloc(sizeof(*link), GFP_KERNEL);
2357+
if (!link) {
2358+
err = -ENOMEM;
2359+
goto error;
2360+
}
2361+
2362+
bpf_link_init(&link->link, BPF_LINK_TYPE_KPROBE_MULTI,
2363+
&bpf_kprobe_multi_link_lops, prog);
2364+
2365+
err = bpf_link_prime(&link->link, &link_primer);
2366+
if (err)
2367+
goto error;
2368+
2369+
if (flags & BPF_F_KPROBE_MULTI_RETURN)
2370+
link->fp.exit_handler = kprobe_multi_link_handler;
2371+
else
2372+
link->fp.entry_handler = kprobe_multi_link_handler;
2373+
2374+
link->addrs = addrs;
2375+
2376+
err = register_fprobe_ips(&link->fp, addrs, cnt);
2377+
if (err) {
2378+
bpf_link_cleanup(&link_primer);
2379+
return err;
2380+
}
2381+
2382+
return bpf_link_settle(&link_primer);
2383+
2384+
error:
2385+
kfree(link);
2386+
kvfree(addrs);
2387+
return err;
2388+
}
2389+
#else /* !CONFIG_FPROBE */
2390+
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
2391+
{
2392+
return -EOPNOTSUPP;
2393+
}
2394+
#endif

0 commit comments

Comments
 (0)