Skip to content

Commit ab51e15

Browse files
mhiramatAlexei Starovoitov
authored andcommitted
fprobe: Introduce FPROBE_FL_KPROBE_SHARED flag for fprobe
Introduce FPROBE_FL_KPROBE_SHARED flag for sharing fprobe callback with kprobes safely from the viewpoint of recursion. Since the recursion safety of the fprobe (and ftrace) is a bit different from the kprobes, this may cause an issue if user wants to run the same code from the fprobe and the kprobes. The kprobes has per-cpu 'current_kprobe' variable which protects the kprobe handler from recursion in any case. On the other hand, the fprobe uses only ftrace_test_recursion_trylock(), which will allow interrupt context calls another (or same) fprobe during the fprobe user handler is running. This is not a matter in cases if the common callback shared among the kprobes and the fprobe has its own recursion detection, or it can handle the recursion in the different contexts (normal/interrupt/NMI.) But if it relies on the 'current_kprobe' recursion lock, it has to check kprobe_running() and use kprobe_busy_*() APIs. Fprobe has FPROBE_FL_KPROBE_SHARED flag to do this. If your common callback code will be shared with kprobes, please set FPROBE_FL_KPROBE_SHARED *before* registering the fprobe, like; fprobe.flags = FPROBE_FL_KPROBE_SHARED; register_fprobe(&fprobe, "func*", NULL); This will protect your common callback from the nested call. Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]> Tested-by: Steven Rostedt (Google) <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/164735293127.1084943.15687374237275817599.stgit@devnote2
1 parent 6ee64cc commit ab51e15

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

include/linux/fprobe.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,25 @@ struct fprobe {
3434
void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, struct pt_regs *regs);
3535
};
3636

37+
/* This fprobe is soft-disabled. */
3738
#define FPROBE_FL_DISABLED 1
3839

40+
/*
41+
* This fprobe handler will be shared with kprobes.
42+
* This flag must be set before registering.
43+
*/
44+
#define FPROBE_FL_KPROBE_SHARED 2
45+
3946
static inline bool fprobe_disabled(struct fprobe *fp)
4047
{
4148
return (fp) ? fp->flags & FPROBE_FL_DISABLED : false;
4249
}
4350

51+
static inline bool fprobe_shared_with_kprobes(struct fprobe *fp)
52+
{
53+
return (fp) ? fp->flags & FPROBE_FL_KPROBE_SHARED : false;
54+
}
55+
4456
#ifdef CONFIG_FPROBE
4557
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter);
4658
int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num);

include/linux/kprobes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ static inline struct kprobe *kprobe_running(void)
427427
{
428428
return NULL;
429429
}
430+
#define kprobe_busy_begin() do {} while (0)
431+
#define kprobe_busy_end() do {} while (0)
432+
430433
static inline int register_kprobe(struct kprobe *p)
431434
{
432435
return -EOPNOTSUPP;

kernel/trace/fprobe.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
5656
}
5757
NOKPROBE_SYMBOL(fprobe_handler);
5858

59+
static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
60+
struct ftrace_ops *ops, struct ftrace_regs *fregs)
61+
{
62+
struct fprobe *fp = container_of(ops, struct fprobe, ops);
63+
64+
if (unlikely(kprobe_running())) {
65+
fp->nmissed++;
66+
return;
67+
}
68+
kprobe_busy_begin();
69+
fprobe_handler(ip, parent_ip, ops, fregs);
70+
kprobe_busy_end();
71+
}
72+
5973
static void fprobe_exit_handler(struct rethook_node *rh, void *data,
6074
struct pt_regs *regs)
6175
{
@@ -110,7 +124,10 @@ static unsigned long *get_ftrace_locations(const char **syms, int num)
110124
static void fprobe_init(struct fprobe *fp)
111125
{
112126
fp->nmissed = 0;
113-
fp->ops.func = fprobe_handler;
127+
if (fprobe_shared_with_kprobes(fp))
128+
fp->ops.func = fprobe_kprobe_handler;
129+
else
130+
fp->ops.func = fprobe_handler;
114131
fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
115132
}
116133

0 commit comments

Comments
 (0)