History

ftrace: Fix function_graph tracer interaction with BPF trampoline

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.

Alexei Starovoitov committed 6 years ago

Commit ff205761 parent a61f8104 files · +21 −26Browse files

Unified · Split

4 files
  1. arch/x86/kernel/ftrace.c +0 −14
  2. include/linux/ftrace.h +5 −0
  3. kernel/trace/fgraph.c +9 −0
  4. kernel/trace/ftrace.c +7 −12
arch/x86/kernel/ftrace.c+0−14
@@ -1042,20 +1042,6 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, · 1041 unmodified lines
1042 if (unlikely(atomic_read(&current->tracing_graph_pause)))1042 if (unlikely(atomic_read(&current->tracing_graph_pause)))
1043 return;1043 return;
1044 1044
1045 /*
1046 * If the return location is actually pointing directly to
1047 * the start of a direct trampoline (if we trace the trampoline
1048 * it will still be offset by MCOUNT_INSN_SIZE), then the
1049 * return address is actually off by one word, and we
1050 * need to adjust for that.
1051 */
1052 if (ftrace_direct_func_count) {
1053 if (ftrace_find_direct_func(self_addr + MCOUNT_INSN_SIZE)) {
1054 self_addr = *parent;
1055 parent++;
1056 }
1057 }
1058
1059 /*1045 /*
1060 * Protect against fault, even if it shouldn't1046 * Protect against fault, even if it shouldn't
1061 * happen. This tool is too much intrusive to1047 * happen. This tool is too much intrusive to
include/linux/ftrace.h+5−0
@@ -264,6 +264,7 @@ int ftrace_modify_direct_caller(struct ftrace_func_entry *entry, · 263 unmodified lines
264 struct dyn_ftrace *rec,264 struct dyn_ftrace *rec,
265 unsigned long old_addr,265 unsigned long old_addr,
266 unsigned long new_addr);266 unsigned long new_addr);
267unsigned long ftrace_find_rec_direct(unsigned long ip);
267#else268#else
268# define ftrace_direct_func_count 0269# define ftrace_direct_func_count 0
269static inline int register_ftrace_direct(unsigned long ip, unsigned long addr)270static inline int register_ftrace_direct(unsigned long ip, unsigned long addr)
@@ -290,6 +291,10 @@ static inline int ftrace_modify_direct_caller(struct ftrace_func_entry *entry, · 20 unmodified lines
290{291{
291 return -ENODEV;292 return -ENODEV;
292}293}
294static inline unsigned long ftrace_find_rec_direct(unsigned long ip)
295{
296 return 0;
297}
293#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */298#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
294 299
295#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS300#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
kernel/trace/fgraph.c+9−0
@@ -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
kernel/trace/ftrace.c+7−12
@@ -2364,7 +2364,7 @@ int ftrace_direct_func_count; · 2363 unmodified lines
2364 * Search the direct_functions hash to see if the given instruction pointer2364 * 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 */
2367static unsigned long find_rec_direct(unsigned long ip)2367unsigned 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_REGS2393 | FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS
2394 | FTRACE_OPS_FL_PERMANENT,2394 | FTRACE_OPS_FL_PERMANENT,
2395};2395};
2396#else
2397static 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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t