Depending on type of BPF programs served by BPF trampoline it can call original function. In such case the trampoline will skip one stack frame while returning. That will confuse function_graph tracer and will cause crashes with bad RIP. Teach graph tracer to skip functions that have BPF trampoline attached.
Showing the changes to kernel (2 of 4 files). Show all
Unified · Split
| @@ -101,6 +101,15 @@ int function_graph_enter(unsigned long ret, unsigned long func, · 100 unmodified lines | |||
| 101 | { | 101 | { |
| 102 | struct ftrace_graph_ent trace; | 102 | struct ftrace_graph_ent trace; |
| 103 | 103 | ||
| 104 | /* | ||
| 105 | * Skip graph tracing if the return location is served by direct trampoline, | ||
| 106 | * since call sequence and return addresses is unpredicatable anymore. | ||
| 107 | * Ex: BPF trampoline may call original function and may skip frame | ||
| 108 | * depending on type of BPF programs attached. | ||
| 109 | */ | ||
| 110 | if (ftrace_direct_func_count && | ||
| 111 | ftrace_find_rec_direct(ret - MCOUNT_INSN_SIZE)) | ||
| 112 | return -EBUSY; | ||
| 104 | trace.func = func; | 113 | trace.func = func; |
| 105 | trace.depth = ++current->curr_ret_depth; | 114 | trace.depth = ++current->curr_ret_depth; |
| 106 | 115 | ||
| @@ -2364,7 +2364,7 @@ int ftrace_direct_func_count; · 2363 unmodified lines | |||
| 2364 | * Search the direct_functions hash to see if the given instruction pointer | 2364 | * Search the direct_functions hash to see if the given instruction pointer |
| 2365 | * has a direct caller attached to it. | 2365 | * has a direct caller attached to it. |
| 2366 | */ | 2366 | */ |
| 2367 | static unsigned long find_rec_direct(unsigned long ip) | 2367 | unsigned long ftrace_find_rec_direct(unsigned long ip) |
| 2368 | { | 2368 | { |
| 2369 | struct ftrace_func_entry *entry; | 2369 | struct ftrace_func_entry *entry; |
| 2370 | 2370 | ||
| @@ -2380,7 +2380,7 @@ static void call_direct_funcs(unsigned long ip, unsigned long pip, · 9 unmodified lines | |||
| 2380 | { | 2380 | { |
| 2381 | unsigned long addr; | 2381 | unsigned long addr; |
| 2382 | 2382 | ||
| 2383 | addr = find_rec_direct(ip); | 2383 | addr = ftrace_find_rec_direct(ip); |
| 2384 | if (!addr) | 2384 | if (!addr) |
| 2385 | return; | 2385 | return; |
| 2386 | 2386 | ||
| @@ -2393,11 +2393,6 @@ struct ftrace_ops direct_ops = { · 6 unmodified lines | |||
| 2393 | | FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS | 2393 | | FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS |
| 2394 | | FTRACE_OPS_FL_PERMANENT, | 2394 | | FTRACE_OPS_FL_PERMANENT, |
| 2395 | }; | 2395 | }; |
| 2396 | #else | ||
| 2397 | static inline unsigned long find_rec_direct(unsigned long ip) | ||
| 2398 | { | ||
| 2399 | return 0; | ||
| 2400 | } | ||
| 2401 | #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ | 2396 | #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ |
| 2402 | 2397 | ||
| 2403 | /** | 2398 | /** |
| @@ -2417,7 +2412,7 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) · 13 unmodified lines | |||
| 2417 | 2412 | ||
| 2418 | if ((rec->flags & FTRACE_FL_DIRECT) && | 2413 | if ((rec->flags & FTRACE_FL_DIRECT) && |
| 2419 | (ftrace_rec_count(rec) == 1)) { | 2414 | (ftrace_rec_count(rec) == 1)) { |
| 2420 | addr = find_rec_direct(rec->ip); | 2415 | addr = ftrace_find_rec_direct(rec->ip); |
| 2421 | if (addr) | 2416 | if (addr) |
| 2422 | return addr; | 2417 | return addr; |
| 2423 | WARN_ON_ONCE(1); | 2418 | WARN_ON_ONCE(1); |
| @@ -2458,7 +2453,7 @@ unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) · 34 unmodified lines | |||
| 2458 | 2453 | ||
| 2459 | /* Direct calls take precedence over trampolines */ | 2454 | /* Direct calls take precedence over trampolines */ |
| 2460 | if (rec->flags & FTRACE_FL_DIRECT_EN) { | 2455 | if (rec->flags & FTRACE_FL_DIRECT_EN) { |
| 2461 | addr = find_rec_direct(rec->ip); | 2456 | addr = ftrace_find_rec_direct(rec->ip); |
| 2462 | if (addr) | 2457 | if (addr) |
| 2463 | return addr; | 2458 | return addr; |
| 2464 | WARN_ON_ONCE(1); | 2459 | WARN_ON_ONCE(1); |
| @@ -3604,7 +3599,7 @@ static int t_show(struct seq_file *m, void *v) · 1139 unmodified lines | |||
| 3604 | if (rec->flags & FTRACE_FL_DIRECT) { | 3599 | if (rec->flags & FTRACE_FL_DIRECT) { |
| 3605 | unsigned long direct; | 3600 | unsigned long direct; |
| 3606 | 3601 | ||
| 3607 | direct = find_rec_direct(rec->ip); | 3602 | direct = ftrace_find_rec_direct(rec->ip); |
| 3608 | if (direct) | 3603 | if (direct) |
| 3609 | seq_printf(m, "\n\tdirect-->%pS", (void *)direct); | 3604 | seq_printf(m, "\n\tdirect-->%pS", (void *)direct); |
| 3610 | } | 3605 | } |
| @@ -5008,7 +5003,7 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr) · 1397 unmodified lines | |||
| 5008 | mutex_lock(&direct_mutex); | 5003 | mutex_lock(&direct_mutex); |
| 5009 | 5004 | ||
| 5010 | /* See if there's a direct function at @ip already */ | 5005 | /* See if there's a direct function at @ip already */ |
| 5011 | if (find_rec_direct(ip)) | 5006 | if (ftrace_find_rec_direct(ip)) |
| 5012 | goto out_unlock; | 5007 | goto out_unlock; |
| 5013 | 5008 | ||
| 5014 | ret = -ENODEV; | 5009 | ret = -ENODEV; |
| @@ -5027,7 +5022,7 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr) · 12 unmodified lines | |||
| 5027 | if (ip != rec->ip) { | 5022 | if (ip != rec->ip) { |
| 5028 | ip = rec->ip; | 5023 | ip = rec->ip; |
| 5029 | /* Need to check this ip for a direct. */ | 5024 | /* Need to check this ip for a direct. */ |
| 5030 | if (find_rec_direct(ip)) | 5025 | if (ftrace_find_rec_direct(ip)) |
| 5031 | goto out_unlock; | 5026 | goto out_unlock; |
| 5032 | } | 5027 | } |
| 5033 | 5028 | ||