Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf evsel: Fix buffer overflow while freeing events

Fix buffer overflow for:

% perf stat -e msr/tsc/,cstate_core/c7-residency/ true

that causes glibc free list corruption. For some reason it doesn't
trigger in valgrind, but it is visible in AS:

=================================================================
==32681==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000003f5c at pc 0x0000005671ef bp 0x7ffdaaac9ac0 sp 0x7ffdaaac9ab0
READ of size 4 at 0x603000003f5c thread T0
#0 0x5671ee in perf_evsel__close_fd util/evsel.c:1196
#1 0x56c57a in perf_evsel__close util/evsel.c:1717
#2 0x55ed5f in perf_evlist__close util/evlist.c:1631
#3 0x4647e1 in __run_perf_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:749
#4 0x4648e3 in run_perf_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:767
#5 0x46e1bc in cmd_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:2785
#6 0x52f83d in run_builtin /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:296
#7 0x52fd49 in handle_internal_command /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:348
#8 0x5300de in run_argv /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:392
#9 0x5308f3 in main /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:530
#10 0x7f0672d13400 in __libc_start_main (/lib64/libc.so.6+0x20400)
#11 0x428419 in _start (/home/ak/hle/obj-perf/perf+0x428419)

0x603000003f5c is located 0 bytes to the right of 28-byte region [0x603000003f40,0x603000003f5c)
allocated by thread T0 here:
#0 0x7f0675139020 in calloc (/lib64/libasan.so.3+0xc7020)
#1 0x648a2d in zalloc util/util.h:23
#2 0x648a88 in xyarray__new util/xyarray.c:9
#3 0x566419 in perf_evsel__alloc_fd util/evsel.c:1039
#4 0x56b427 in perf_evsel__open util/evsel.c:1529
#5 0x56c620 in perf_evsel__open_per_thread util/evsel.c:1730
#6 0x461dea in create_perf_stat_counter /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:263
#7 0x4637d7 in __run_perf_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:600
#8 0x4648e3 in run_perf_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:767
#9 0x46e1bc in cmd_stat /home/ak/hle/linux-hle-2.6/tools/perf/builtin-stat.c:2785
#10 0x52f83d in run_builtin /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:296
#11 0x52fd49 in handle_internal_command /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:348
#12 0x5300de in run_argv /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:392
#13 0x5308f3 in main /home/ak/hle/linux-hle-2.6/tools/perf/perf.c:530
#14 0x7f0672d13400 in __libc_start_main (/lib64/libc.so.6+0x20400)

The event is allocated with cpus == 1, but freed with cpus == real number
When the evsel close function walks the file descriptors it exceeds the
fd xyarray boundaries and reads random memory.

v2:

Now that xyarrays save their original dimensions we can use these to
iterate the two dimensional fd arrays. Fix some users (close, ioctl) in
evsel.c to use these fields directly. This allows simplifying the code
and dropping quite a few function arguments. Adjust all callers by
removing the unneeded arguments.

The actual perf event reading still uses the original values from the
evsel list.

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Link: http://lkml.kernel.org/r/20170811232634.30465-2-andi@firstfloor.org
[ Fix up xy_max_[xy]() -> xyarray__max_[xy]() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Andi Kleen and committed by
Arnaldo Carvalho de Melo
475fb533 d74be476

+20 -40
+1 -1
tools/perf/tests/openat-syscall-all-cpus.c
··· 115 115 116 116 perf_evsel__free_counts(evsel); 117 117 out_close_fd: 118 - perf_evsel__close_fd(evsel, 1, threads->nr); 118 + perf_evsel__close_fd(evsel); 119 119 out_evsel_delete: 120 120 perf_evsel__delete(evsel); 121 121 out_thread_map_delete:
+1 -1
tools/perf/tests/openat-syscall.c
··· 56 56 57 57 err = 0; 58 58 out_close_fd: 59 - perf_evsel__close_fd(evsel, 1, threads->nr); 59 + perf_evsel__close_fd(evsel); 60 60 out_evsel_delete: 61 61 perf_evsel__delete(evsel); 62 62 out_thread_map_delete:
+3 -9
tools/perf/util/evlist.c
··· 1419 1419 { 1420 1420 struct perf_evsel *evsel; 1421 1421 int err = 0; 1422 - const int ncpus = cpu_map__nr(evlist->cpus), 1423 - nthreads = thread_map__nr(evlist->threads); 1424 1422 1425 1423 evlist__for_each_entry(evlist, evsel) { 1426 1424 if (evsel->filter == NULL) ··· 1428 1430 * filters only work for tracepoint event, which doesn't have cpu limit. 1429 1431 * So evlist and evsel should always be same. 1430 1432 */ 1431 - err = perf_evsel__apply_filter(evsel, ncpus, nthreads, evsel->filter); 1433 + err = perf_evsel__apply_filter(evsel, evsel->filter); 1432 1434 if (err) { 1433 1435 *err_evsel = evsel; 1434 1436 break; ··· 1621 1623 void perf_evlist__close(struct perf_evlist *evlist) 1622 1624 { 1623 1625 struct perf_evsel *evsel; 1624 - int ncpus = cpu_map__nr(evlist->cpus); 1625 - int nthreads = thread_map__nr(evlist->threads); 1626 1626 1627 - evlist__for_each_entry_reverse(evlist, evsel) { 1628 - int n = evsel->cpus ? evsel->cpus->nr : ncpus; 1629 - perf_evsel__close(evsel, n, nthreads); 1630 - } 1627 + evlist__for_each_entry_reverse(evlist, evsel) 1628 + perf_evsel__close(evsel); 1631 1629 } 1632 1630 1633 1631 static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
+12 -25
tools/perf/util/evsel.c
··· 1051 1051 return evsel->fd != NULL ? 0 : -ENOMEM; 1052 1052 } 1053 1053 1054 - static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthreads, 1054 + static int perf_evsel__run_ioctl(struct perf_evsel *evsel, 1055 1055 int ioc, void *arg) 1056 1056 { 1057 1057 int cpu, thread; 1058 1058 1059 - if (evsel->system_wide) 1060 - nthreads = 1; 1061 - 1062 - for (cpu = 0; cpu < ncpus; cpu++) { 1063 - for (thread = 0; thread < nthreads; thread++) { 1059 + for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { 1060 + for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { 1064 1061 int fd = FD(evsel, cpu, thread), 1065 1062 err = ioctl(fd, ioc, arg); 1066 1063 ··· 1069 1072 return 0; 1070 1073 } 1071 1074 1072 - int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, 1073 - const char *filter) 1075 + int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) 1074 1076 { 1075 - return perf_evsel__run_ioctl(evsel, ncpus, nthreads, 1077 + return perf_evsel__run_ioctl(evsel, 1076 1078 PERF_EVENT_IOC_SET_FILTER, 1077 1079 (void *)filter); 1078 1080 } ··· 1118 1122 1119 1123 int perf_evsel__enable(struct perf_evsel *evsel) 1120 1124 { 1121 - int nthreads = thread_map__nr(evsel->threads); 1122 - int ncpus = cpu_map__nr(evsel->cpus); 1123 - 1124 - return perf_evsel__run_ioctl(evsel, ncpus, nthreads, 1125 + return perf_evsel__run_ioctl(evsel, 1125 1126 PERF_EVENT_IOC_ENABLE, 1126 1127 0); 1127 1128 } 1128 1129 1129 1130 int perf_evsel__disable(struct perf_evsel *evsel) 1130 1131 { 1131 - int nthreads = thread_map__nr(evsel->threads); 1132 - int ncpus = cpu_map__nr(evsel->cpus); 1133 - 1134 - return perf_evsel__run_ioctl(evsel, ncpus, nthreads, 1132 + return perf_evsel__run_ioctl(evsel, 1135 1133 PERF_EVENT_IOC_DISABLE, 1136 1134 0); 1137 1135 } ··· 1175 1185 } 1176 1186 } 1177 1187 1178 - void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) 1188 + void perf_evsel__close_fd(struct perf_evsel *evsel) 1179 1189 { 1180 1190 int cpu, thread; 1181 1191 1182 - if (evsel->system_wide) 1183 - nthreads = 1; 1184 - 1185 - for (cpu = 0; cpu < ncpus; cpu++) 1186 - for (thread = 0; thread < nthreads; ++thread) { 1192 + for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) 1193 + for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { 1187 1194 close(FD(evsel, cpu, thread)); 1188 1195 FD(evsel, cpu, thread) = -1; 1189 1196 } ··· 1841 1854 return err; 1842 1855 } 1843 1856 1844 - void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads) 1857 + void perf_evsel__close(struct perf_evsel *evsel) 1845 1858 { 1846 1859 if (evsel->fd == NULL) 1847 1860 return; 1848 1861 1849 - perf_evsel__close_fd(evsel, ncpus, nthreads); 1862 + perf_evsel__close_fd(evsel); 1850 1863 perf_evsel__free_fd(evsel); 1851 1864 } 1852 1865
+3 -4
tools/perf/util/evsel.h
··· 226 226 int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); 227 227 228 228 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); 229 - void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); 229 + void perf_evsel__close_fd(struct perf_evsel *evsel); 230 230 231 231 void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, 232 232 enum perf_event_sample_format bit); ··· 246 246 int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter); 247 247 int perf_evsel__append_addr_filter(struct perf_evsel *evsel, 248 248 const char *filter); 249 - int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, 250 - const char *filter); 249 + int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter); 251 250 int perf_evsel__enable(struct perf_evsel *evsel); 252 251 int perf_evsel__disable(struct perf_evsel *evsel); 253 252 ··· 256 257 struct thread_map *threads); 257 258 int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, 258 259 struct thread_map *threads); 259 - void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); 260 + void perf_evsel__close(struct perf_evsel *evsel); 260 261 261 262 struct perf_sample; 262 263