diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-18 02:00:17 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-18 02:15:47 -0400 |
commit | 7522060c95395f479ee4a6af3bbf9e097e92e48f (patch) | |
tree | 3530611cfd45c75c5a1952210edb2953598a9afe | |
parent | b25bcf2f133b1e6216c3d40be394756107d3880f (diff) |
perf report: Add validation of call-chain entries
Add boundary checks for call-chain events. In case of corrupted
entries we could crash otherwise.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/perf_counter.h | 20 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 74 |
2 files changed, 56 insertions, 38 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index eccae437fe37..a7d3a61a59b7 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -337,6 +337,16 @@ enum perf_event_type { | |||
337 | */ | 337 | */ |
338 | }; | 338 | }; |
339 | 339 | ||
340 | #define MAX_STACK_DEPTH 255 | ||
341 | |||
342 | struct perf_callchain_entry { | ||
343 | __u16 nr; | ||
344 | __u16 hv; | ||
345 | __u16 kernel; | ||
346 | __u16 user; | ||
347 | __u64 ip[MAX_STACK_DEPTH]; | ||
348 | }; | ||
349 | |||
340 | #ifdef __KERNEL__ | 350 | #ifdef __KERNEL__ |
341 | /* | 351 | /* |
342 | * Kernel-internal data types and definitions: | 352 | * Kernel-internal data types and definitions: |
@@ -652,16 +662,6 @@ extern void perf_counter_fork(struct task_struct *tsk); | |||
652 | 662 | ||
653 | extern void perf_counter_task_migration(struct task_struct *task, int cpu); | 663 | extern void perf_counter_task_migration(struct task_struct *task, int cpu); |
654 | 664 | ||
655 | #define MAX_STACK_DEPTH 255 | ||
656 | |||
657 | struct perf_callchain_entry { | ||
658 | u16 nr; | ||
659 | u16 hv; | ||
660 | u16 kernel; | ||
661 | u16 user; | ||
662 | u64 ip[MAX_STACK_DEPTH]; | ||
663 | }; | ||
664 | |||
665 | extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); | 665 | extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); |
666 | 666 | ||
667 | extern int sysctl_perf_counter_paranoid; | 667 | extern int sysctl_perf_counter_paranoid; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 986834623b43..e14e98676171 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -39,6 +39,8 @@ static int dump_trace = 0; | |||
39 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) | 39 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) |
40 | 40 | ||
41 | static int verbose; | 41 | static int verbose; |
42 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) | ||
43 | |||
42 | static int full_paths; | 44 | static int full_paths; |
43 | 45 | ||
44 | static unsigned long page_size; | 46 | static unsigned long page_size; |
@@ -47,14 +49,6 @@ static unsigned long mmap_window = 32; | |||
47 | static char *parent_pattern = "^sys_|^do_page_fault"; | 49 | static char *parent_pattern = "^sys_|^do_page_fault"; |
48 | static regex_t parent_regex; | 50 | static regex_t parent_regex; |
49 | 51 | ||
50 | struct ip_chain_event { | ||
51 | __u16 nr; | ||
52 | __u16 hv; | ||
53 | __u16 kernel; | ||
54 | __u16 user; | ||
55 | __u64 ips[]; | ||
56 | }; | ||
57 | |||
58 | struct ip_event { | 52 | struct ip_event { |
59 | struct perf_event_header header; | 53 | struct perf_event_header header; |
60 | __u64 ip; | 54 | __u64 ip; |
@@ -131,15 +125,11 @@ static struct dso *dsos__findnew(const char *name) | |||
131 | 125 | ||
132 | nr = dso__load(dso, NULL, verbose); | 126 | nr = dso__load(dso, NULL, verbose); |
133 | if (nr < 0) { | 127 | if (nr < 0) { |
134 | if (verbose) | 128 | eprintf("Failed to open: %s\n", name); |
135 | fprintf(stderr, "Failed to open: %s\n", name); | ||
136 | goto out_delete_dso; | 129 | goto out_delete_dso; |
137 | } | 130 | } |
138 | if (!nr && verbose) { | 131 | if (!nr) |
139 | fprintf(stderr, | 132 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); |
140 | "No symbols found in: %s, maybe install a debug package?\n", | ||
141 | name); | ||
142 | } | ||
143 | 133 | ||
144 | dsos__add(dso); | 134 | dsos__add(dso); |
145 | 135 | ||
@@ -844,7 +834,7 @@ static struct symbol *call__match(struct symbol *sym) | |||
844 | 834 | ||
845 | static int | 835 | static int |
846 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 836 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
847 | struct symbol *sym, __u64 ip, struct ip_chain_event *chain, | 837 | struct symbol *sym, __u64 ip, struct perf_callchain_entry *chain, |
848 | char level, __u64 count) | 838 | char level, __u64 count) |
849 | { | 839 | { |
850 | struct rb_node **p = &hist.rb_node; | 840 | struct rb_node **p = &hist.rb_node; |
@@ -868,7 +858,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
868 | __u64 ip; | 858 | __u64 ip; |
869 | 859 | ||
870 | for (i = 0; i < chain->kernel; i++) { | 860 | for (i = 0; i < chain->kernel; i++) { |
871 | ip = chain->ips[nr + i]; | 861 | ip = chain->ip[nr + i]; |
872 | dso = kernel_dso; | 862 | dso = kernel_dso; |
873 | sym = resolve_symbol(thread, NULL, &dso, &ip); | 863 | sym = resolve_symbol(thread, NULL, &dso, &ip); |
874 | entry.parent = call__match(sym); | 864 | entry.parent = call__match(sym); |
@@ -878,7 +868,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
878 | nr += i; | 868 | nr += i; |
879 | 869 | ||
880 | for (i = 0; i < chain->user; i++) { | 870 | for (i = 0; i < chain->user; i++) { |
881 | ip = chain->ips[nr + i]; | 871 | ip = chain->ip[nr + i]; |
882 | sym = resolve_symbol(thread, NULL, NULL, &ip); | 872 | sym = resolve_symbol(thread, NULL, NULL, &ip); |
883 | entry.parent = call__match(sym); | 873 | entry.parent = call__match(sym); |
884 | if (entry.parent) | 874 | if (entry.parent) |
@@ -1080,6 +1070,30 @@ static unsigned long total = 0, | |||
1080 | total_fork = 0, | 1070 | total_fork = 0, |
1081 | total_unknown = 0; | 1071 | total_unknown = 0; |
1082 | 1072 | ||
1073 | static int validate_chain(struct perf_callchain_entry *chain, event_t *event) | ||
1074 | { | ||
1075 | unsigned int chain_size; | ||
1076 | |||
1077 | if (chain->nr > MAX_STACK_DEPTH) | ||
1078 | return -1; | ||
1079 | if (chain->hv > MAX_STACK_DEPTH) | ||
1080 | return -1; | ||
1081 | if (chain->kernel > MAX_STACK_DEPTH) | ||
1082 | return -1; | ||
1083 | if (chain->user > MAX_STACK_DEPTH) | ||
1084 | return -1; | ||
1085 | if (chain->hv + chain->kernel + chain->user != chain->nr) | ||
1086 | return -1; | ||
1087 | |||
1088 | chain_size = event->header.size; | ||
1089 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | ||
1090 | |||
1091 | if (chain->nr*sizeof(__u64) > chain_size) | ||
1092 | return -1; | ||
1093 | |||
1094 | return 0; | ||
1095 | } | ||
1096 | |||
1083 | static int | 1097 | static int |
1084 | process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | 1098 | process_overflow_event(event_t *event, unsigned long offset, unsigned long head) |
1085 | { | 1099 | { |
@@ -1091,7 +1105,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1091 | __u64 period = 1; | 1105 | __u64 period = 1; |
1092 | struct map *map = NULL; | 1106 | struct map *map = NULL; |
1093 | void *more_data = event->ip.__more_data; | 1107 | void *more_data = event->ip.__more_data; |
1094 | struct ip_chain_event *chain = NULL; | 1108 | struct perf_callchain_entry *chain = NULL; |
1095 | 1109 | ||
1096 | if (event->header.type & PERF_SAMPLE_PERIOD) { | 1110 | if (event->header.type & PERF_SAMPLE_PERIOD) { |
1097 | period = *(__u64 *)more_data; | 1111 | period = *(__u64 *)more_data; |
@@ -1111,21 +1125,26 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1111 | 1125 | ||
1112 | chain = (void *)more_data; | 1126 | chain = (void *)more_data; |
1113 | 1127 | ||
1114 | if (dump_trace) { | 1128 | dprintf("... chain: u:%d, k:%d, nr:%d\n", |
1115 | dprintf("... chain: u:%d, k:%d, nr:%d\n", | 1129 | chain->user, |
1116 | chain->user, | 1130 | chain->kernel, |
1117 | chain->kernel, | 1131 | chain->nr); |
1118 | chain->nr); | ||
1119 | 1132 | ||
1133 | if (validate_chain(chain, event) < 0) { | ||
1134 | eprintf("call-chain problem with event, skipping it.\n"); | ||
1135 | return 0; | ||
1136 | } | ||
1137 | |||
1138 | if (dump_trace) { | ||
1120 | for (i = 0; i < chain->nr; i++) | 1139 | for (i = 0; i < chain->nr; i++) |
1121 | dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); | 1140 | dprintf("..... %2d: %016Lx\n", i, chain->ip[i]); |
1122 | } | 1141 | } |
1123 | } | 1142 | } |
1124 | 1143 | ||
1125 | dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1144 | dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
1126 | 1145 | ||
1127 | if (thread == NULL) { | 1146 | if (thread == NULL) { |
1128 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 1147 | eprintf("problem processing %d event, skipping it.\n", |
1129 | event->header.type); | 1148 | event->header.type); |
1130 | return -1; | 1149 | return -1; |
1131 | } | 1150 | } |
@@ -1153,8 +1172,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
1153 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1172 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1154 | 1173 | ||
1155 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1174 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
1156 | fprintf(stderr, | 1175 | eprintf("problem incrementing symbol count, skipping event\n"); |
1157 | "problem incrementing symbol count, skipping event\n"); | ||
1158 | return -1; | 1176 | return -1; |
1159 | } | 1177 | } |
1160 | } | 1178 | } |