Skip to content

Commit 03e9ef9

Browse files
liu-song-6Nobody
authored and
Nobody
committed
ftrace_direct (used by bpf trampoline) conflicts with live patch
Hi Steven, We hit an issue with bpf trampoline and kernel live patch on the same function. Basically, we have tracing and live patch on the same function. If we use kprobe (over ftrace) for tracing, it works fine with live patch. However, fentry on the same function does not work with live patch (the one comes later fails to attach). After digging into this, I found this is because bpf trampoline uses register_ftrace_direct, which enables IPMODIFY by default. OTOH, it seems that BPF doesn't really need IPMODIFY. As BPF trampoline does a "goto do_fexit" in jit for BPF_TRAMP_MODIFY_RETURN. IIUC, we can let bpf trampoline and live patch work together with an ipmodify-less version of register_ftrace_direct, like attached below. Does this make sense to you? Did I miss something? Thanks in advance, Song
1 parent 132a6fb commit 03e9ef9

File tree

3 files changed

+71
-9
lines changed

3 files changed

+71
-9
lines changed

include/linux/ftrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ struct dyn_ftrace;
326326
extern int ftrace_direct_func_count;
327327
int register_ftrace_direct(unsigned long ip, unsigned long addr);
328328
int unregister_ftrace_direct(unsigned long ip, unsigned long addr);
329+
int register_ftrace_direct_no_ipmodify(unsigned long ip, unsigned long addr);
330+
int unregister_ftrace_direct_no_ipmodify(unsigned long ip, unsigned long addr);
329331
int modify_ftrace_direct(unsigned long ip, unsigned long old_addr, unsigned long new_addr);
330332
struct ftrace_direct_func *ftrace_find_direct_func(unsigned long addr);
331333
int ftrace_modify_direct_caller(struct ftrace_func_entry *entry,

kernel/bpf/trampoline.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr)
123123
int ret;
124124

125125
if (tr->func.ftrace_managed)
126-
ret = unregister_ftrace_direct((long)ip, (long)old_addr);
126+
ret = unregister_ftrace_direct_no_ipmodify((long)ip, (long)old_addr);
127127
else
128128
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL);
129129

@@ -159,7 +159,7 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
159159
return -ENOENT;
160160

161161
if (tr->func.ftrace_managed)
162-
ret = register_ftrace_direct((long)ip, (long)new_addr);
162+
ret = register_ftrace_direct_no_ipmodify((long)ip, (long)new_addr);
163163
else
164164
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr);
165165

kernel/trace/ftrace.c

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,20 @@ struct ftrace_ops direct_ops = {
24672467
*/
24682468
.trampoline = FTRACE_REGS_ADDR,
24692469
};
2470+
2471+
struct ftrace_ops no_ipmodify_direct_ops = {
2472+
.func = call_direct_funcs,
2473+
.flags = FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS
2474+
| FTRACE_OPS_FL_PERMANENT,
2475+
/*
2476+
* By declaring the main trampoline as this trampoline
2477+
* it will never have one allocated for it. Allocated
2478+
* trampolines should not call direct functions.
2479+
* The direct_ops should only be called by the builtin
2480+
* ftrace_regs_caller trampoline.
2481+
*/
2482+
.trampoline = FTRACE_REGS_ADDR,
2483+
};
24702484
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
24712485

24722486
/**
@@ -5126,6 +5140,9 @@ static struct ftrace_direct_func *ftrace_alloc_direct_func(unsigned long addr)
51265140
return direct;
51275141
}
51285142

5143+
static int __register_ftrace_direct(unsigned long ip, unsigned long addr,
5144+
struct ftrace_ops *ops);
5145+
51295146
/**
51305147
* register_ftrace_direct - Call a custom trampoline directly
51315148
* @ip: The address of the nop at the beginning of a function
@@ -5144,6 +5161,12 @@ static struct ftrace_direct_func *ftrace_alloc_direct_func(unsigned long addr)
51445161
* -ENOMEM - There was an allocation failure.
51455162
*/
51465163
int register_ftrace_direct(unsigned long ip, unsigned long addr)
5164+
{
5165+
return __register_ftrace_direct(ip, addr, &direct_ops);
5166+
}
5167+
5168+
static int __register_ftrace_direct(unsigned long ip, unsigned long addr,
5169+
struct ftrace_ops *ops)
51475170
{
51485171
struct ftrace_direct_func *direct;
51495172
struct ftrace_func_entry *entry;
@@ -5194,14 +5217,14 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr)
51945217
if (!entry)
51955218
goto out_unlock;
51965219

5197-
ret = ftrace_set_filter_ip(&direct_ops, ip, 0, 0);
5220+
ret = ftrace_set_filter_ip(ops, ip, 0, 0);
51985221
if (ret)
51995222
remove_hash_entry(direct_functions, entry);
52005223

5201-
if (!ret && !(direct_ops.flags & FTRACE_OPS_FL_ENABLED)) {
5202-
ret = register_ftrace_function(&direct_ops);
5224+
if (!ret && !(ops->flags & FTRACE_OPS_FL_ENABLED)) {
5225+
ret = register_ftrace_function(ops);
52035226
if (ret)
5204-
ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
5227+
ftrace_set_filter_ip(ops, ip, 1, 0);
52055228
}
52065229

52075230
if (ret) {
@@ -5230,6 +5253,29 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr)
52305253
}
52315254
EXPORT_SYMBOL_GPL(register_ftrace_direct);
52325255

5256+
/**
5257+
* register_ftrace_direct_no_ipmodify - Call a custom trampoline directly.
5258+
* The custom trampoline should not use IP_MODIFY.
5259+
* @ip: The address of the nop at the beginning of a function
5260+
* @addr: The address of the trampoline to call at @ip
5261+
*
5262+
* This is used to connect a direct call from the nop location (@ip)
5263+
* at the start of ftrace traced functions. The location that it calls
5264+
* (@addr) must be able to handle a direct call, and save the parameters
5265+
* of the function being traced, and restore them (or inject new ones
5266+
* if needed), before returning.
5267+
*
5268+
* Returns:
5269+
* 0 on success
5270+
* -EBUSY - Another direct function is already attached (there can be only one)
5271+
* -ENODEV - @ip does not point to a ftrace nop location (or not supported)
5272+
* -ENOMEM - There was an allocation failure.
5273+
*/
5274+
int register_ftrace_direct_no_ipmodify(unsigned long ip, unsigned long addr)
5275+
{
5276+
return __register_ftrace_direct(ip, addr, &no_ipmodify_direct_ops);
5277+
}
5278+
52335279
static struct ftrace_func_entry *find_direct_entry(unsigned long *ip,
52345280
struct dyn_ftrace **recp)
52355281
{
@@ -5257,7 +5303,21 @@ static struct ftrace_func_entry *find_direct_entry(unsigned long *ip,
52575303
return entry;
52585304
}
52595305

5306+
static int __unregister_ftrace_direct(unsigned long ip, unsigned long addr,
5307+
struct ftrace_ops *ops);
5308+
52605309
int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
5310+
{
5311+
return __unregister_ftrace_direct(ip, addr, &direct_ops);
5312+
}
5313+
5314+
int unregister_ftrace_direct_no_ipmodify(unsigned long ip, unsigned long addr)
5315+
{
5316+
return __unregister_ftrace_direct(ip, addr, &no_ipmodify_direct_ops);
5317+
}
5318+
5319+
static int __unregister_ftrace_direct(unsigned long ip, unsigned long addr,
5320+
struct ftrace_ops *ops)
52615321
{
52625322
struct ftrace_direct_func *direct;
52635323
struct ftrace_func_entry *entry;
@@ -5274,11 +5334,11 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
52745334
if (!entry)
52755335
goto out_unlock;
52765336

5277-
hash = direct_ops.func_hash->filter_hash;
5337+
hash = ops->func_hash->filter_hash;
52785338
if (hash->count == 1)
5279-
unregister_ftrace_function(&direct_ops);
5339+
unregister_ftrace_function(ops);
52805340

5281-
ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
5341+
ret = ftrace_set_filter_ip(ops, ip, 1, 0);
52825342

52835343
WARN_ON(ret);
52845344

0 commit comments

Comments
 (0)