From 8aa8a7c80ccdfac2df5ee48a51a4a7bee2143d4f Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Tue, 13 Mar 2012 16:51:02 +0100 Subject: perf record: Fix buffer overrun bug in tracepoint_id_to_path() This patch fixes a buffer overrun bug in tracepoint_id_to_path(). The bug manisfested itself as a memory error reported by perf record. I ran into it with perf sched: $ perf sched rec noploop 2 noploop for 2 seconds [ perf record: Woken up 14 times to write data ] [ perf record: Captured and wrote 42.701 MB perf.data (~1865622 samples) ] Fatal: No memory to alloc tracepoints list It turned out that tracepoint_id_to_path() was reading the tracepoint id using read() but the buffer was not large enough to include the \n terminator for id with 4 digits or more. The patch fixes the problem by extending the buffer to a more reasonable size covering all possible id length include \n terminator. Note that atoll() stops at the first non digit character, thus it is not necessary to clear the buffer between each read. Signed-off-by: Stephane Eranian Acked-by: Arnaldo Carvalho de Melo Acked-by: Peter Zijlstra Cc: fweisbec@gmail.com Cc: dsahern@gmail.com Link: http://lkml.kernel.org/r/20120313155102.GA6465@quad Signed-off-by: Ingo Molnar --- tools/perf/util/parse-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b029296..c7a6f6f 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -165,7 +165,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) struct tracepoint_path *path = NULL; DIR *sys_dir, *evt_dir; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; - char id_buf[4]; + char id_buf[24]; int fd; u64 id; char evt_path[MAXPATHLEN]; -- cgit v1.1 From b832796caa1fda8516464a003c8c7cc547bc20c2 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 7 Mar 2012 11:42:49 +1100 Subject: perf tools: Incorrect use of snprintf results in SEGV I have a workload where perf top scribbles over the stack and we SEGV. What makes it interesting is that an snprintf is causing this. The workload is a c++ gem that has method names over 3000 characters long, but snprintf is designed to avoid overrunning buffers. So what went wrong? The problem is we assume snprintf returns the number of characters written: ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); ... ret += repsep_snprintf(bf + ret, size - ret, "%s", self->ms.sym->name); Unfortunately this is not how snprintf works. snprintf returns the number of characters that would have been written if there was enough space. In the above case, if the first snprintf returns a value larger than size, we pass a negative size into the second snprintf and happily scribble over the stack. If you have 3000 character c++ methods thats a lot of stack to trample. This patch fixes repsep_snprintf by clamping the value at size - 1 which is the maximum snprintf can write before adding the NULL terminator. I get the sinking feeling that there are a lot of other uses of snprintf that have this same bug, we should audit them all. Cc: David Ahern Cc: Eric B Munson Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Yanmin Zhang Cc: stable@kernel.org Link: http://lkml.kernel.org/r/20120307114249.44275ca3@kryten Signed-off-by: Anton Blanchard Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 16da30d..076c9d4 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -33,6 +33,9 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) } } va_end(ap); + + if (n >= (int)size) + return size - 1; return n; } -- cgit v1.1 From e7f01d1e3d8d501deb8abeaa269d5d48a703b8b0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 14 Mar 2012 12:29:29 -0300 Subject: perf tools: Use scnprintf where applicable Several places were expecting that the value returned was the number of characters printed, not what would be printed if there was space. Fix it by using the scnprintf and vscnprintf variants we inherited from the kernel sources. Some corner cases where the number of printed characters were not accounted were fixed too. Reported-by: Anton Blanchard Cc: Anton Blanchard Cc: Eric B Munson Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Yanmin Zhang Cc: stable@kernel.org Link: http://lkml.kernel.org/n/tip-kwxo2eh29cxmd8ilixi2005x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/util/header.c | 2 +- tools/perf/arch/x86/util/header.c | 2 +- tools/perf/util/color.c | 9 +++++---- tools/perf/util/header.c | 4 ++-- tools/perf/util/hist.c | 30 +++++++++++++++--------------- tools/perf/util/strbuf.c | 7 ++++--- tools/perf/util/ui/browsers/hists.c | 12 ++++++------ tools/perf/util/ui/helpline.c | 2 +- 8 files changed, 35 insertions(+), 33 deletions(-) (limited to 'tools/perf') diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index eba80c2..2f7073d 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c @@ -25,7 +25,7 @@ get_cpuid(char *buffer, size_t sz) pvr = mfspr(SPRN_PVR); - nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); + nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); /* look for end marker to ensure the entire data fit */ if (strchr(buffer, '$')) { diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index f940060..146d12a 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c @@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz) if (family >= 0x6) model += ((a >> 16) & 0xf) << 4; } - nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); + nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); /* look for end marker to ensure the entire data fit */ if (strchr(buffer, '$')) { diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 521c38a7..11e46da 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -1,3 +1,4 @@ +#include #include "cache.h" #include "color.h" @@ -182,12 +183,12 @@ static int __color_vsnprintf(char *bf, size_t size, const char *color, } if (perf_use_color_default && *color) - r += snprintf(bf, size, "%s", color); - r += vsnprintf(bf + r, size - r, fmt, args); + r += scnprintf(bf, size, "%s", color); + r += vscnprintf(bf + r, size - r, fmt, args); if (perf_use_color_default && *color) - r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); + r += scnprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); if (trail) - r += snprintf(bf + r, size - r, "%s", trail); + r += scnprintf(bf + r, size - r, "%s", trail); return r; } diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index ecd7f4d..14bb035 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -280,7 +280,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, if (realname == NULL || filename == NULL || linkname == NULL) goto out_free; - len = snprintf(filename, size, "%s%s%s", + len = scnprintf(filename, size, "%s%s%s", debugdir, is_kallsyms ? "/" : "", realname); if (mkdir_p(filename, 0755)) goto out_free; @@ -295,7 +295,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, goto out_free; } - len = snprintf(linkname, size, "%s/.build-id/%.2s", + len = scnprintf(linkname, size, "%s/.build-id/%.2s", debugdir, sbuild_id); if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6f505d1..e11e482 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -768,7 +768,7 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, sep ? "%.2f" : " %6.2f%%", (period * 100.0) / total); else - ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", + ret = scnprintf(s, size, sep ? "%.2f" : " %6.2f%%", (period * 100.0) / total); if (symbol_conf.show_cpu_utilization) { ret += percent_color_snprintf(s + ret, size - ret, @@ -791,20 +791,20 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, } } } else - ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); + ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); + ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); else - ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); + ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events); } if (symbol_conf.show_total_period) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); + ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); else - ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); + ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period); } if (pair_hists) { @@ -819,25 +819,25 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, diff = new_percent - old_percent; if (fabs(diff) >= 0.01) - snprintf(bf, sizeof(bf), "%+4.2F%%", diff); + ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); else - snprintf(bf, sizeof(bf), " "); + ret += scnprintf(bf, sizeof(bf), " "); if (sep) - ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); + ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += snprintf(s + ret, size - ret, "%11.11s", bf); + ret += scnprintf(s + ret, size - ret, "%11.11s", bf); if (show_displacement) { if (displacement) - snprintf(bf, sizeof(bf), "%+4ld", displacement); + ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement); else - snprintf(bf, sizeof(bf), " "); + ret += scnprintf(bf, sizeof(bf), " "); if (sep) - ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); + ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += snprintf(s + ret, size - ret, "%6.6s", bf); + ret += scnprintf(s + ret, size - ret, "%6.6s", bf); } } @@ -855,7 +855,7 @@ int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, if (se->elide) continue; - ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); + ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); ret += se->se_snprintf(he, s + ret, size - ret, hists__col_len(hists, se->se_width_idx)); } diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 92e0685..2eeb51b 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -1,4 +1,5 @@ #include "cache.h" +#include int prefixcmp(const char *str, const char *prefix) { @@ -89,14 +90,14 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) if (!strbuf_avail(sb)) strbuf_grow(sb, 64); va_start(ap, fmt); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); if (len < 0) - die("your vsnprintf is broken"); + die("your vscnprintf is broken"); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); if (len > strbuf_avail(sb)) { die("this should not happen, your snprintf is broken"); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index e81aef1..bb9197c 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -837,15 +837,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; nr_events = convert_unit(nr_events, &unit); - printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); + printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); if (thread) - printed += snprintf(bf + printed, size - printed, + printed += scnprintf(bf + printed, size - printed, ", Thread: %s(%d)", (thread->comm_set ? thread->comm : ""), thread->pid); if (dso) - printed += snprintf(bf + printed, size - printed, + printed += scnprintf(bf + printed, size - printed, ", DSO: %s", dso->short_name); return printed; } @@ -1095,7 +1095,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, HE_COLORSET_NORMAL); nr_events = convert_unit(nr_events, &unit); - printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, + printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, unit, unit == ' ' ? "" : " ", ev_name); slsmg_printf("%s", bf); @@ -1105,8 +1105,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser, if (!current_entry) ui_browser__set_color(browser, HE_COLORSET_TOP); nr_events = convert_unit(nr_events, &unit); - snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, - unit, unit == ' ' ? "" : " "); + printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", + nr_events, unit, unit == ' ' ? "" : " "); warn = bf; } diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 4f48f59..2f950c2 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -64,7 +64,7 @@ int ui_helpline__show_help(const char *format, va_list ap) static int backlog; pthread_mutex_lock(&ui__lock); - ret = vsnprintf(ui_helpline__last_msg + backlog, + ret = vscnprintf(ui_helpline__last_msg + backlog, sizeof(ui_helpline__last_msg) - backlog, format, ap); backlog += ret; -- cgit v1.1 From eae7a755ee81129370c8f555b0d5672e6673735d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 14 Mar 2012 12:42:34 -0300 Subject: perf tools, x86: Build perf on older user-space as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ancient systems I get this build failure: util/../../../arch/x86/include/asm/unistd.h:67:29: error: asm/unistd_64.h: No such file or directory In file included from util/cache.h:7, from builtin-test.c:8: util/../perf.h: In function ‘sys_perf_event_open’:In file included from util/../perf.h:16 perf.h:170: error: ‘__NR_perf_event_open’ undeclared (first use in this function) The reason is that this old system does not have the split unistd.h headers yet, from which to pick up the syscall definitions. Add the syscall numbers to the already existing i386 and x86_64 blocks in perf.h, and also provide empty include file stubs. With this patch perf builds and works fine on 5 years old user-space as well. Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/n/tip-jctwg64le1w47tuaoeyftsg9@git.kernel.org Signed-off-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 ++ tools/perf/perf.h | 6 ++++++ tools/perf/util/include/asm/unistd_32.h | 1 + tools/perf/util/include/asm/unistd_64.h | 1 + 4 files changed, 10 insertions(+) create mode 100644 tools/perf/util/include/asm/unistd_32.h create mode 100644 tools/perf/util/include/asm/unistd_64.h (limited to 'tools/perf') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7c12650..8a4b9bc 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -249,6 +249,8 @@ LIB_H += util/include/asm/uaccess.h LIB_H += util/include/dwarf-regs.h LIB_H += util/include/asm/dwarf2.h LIB_H += util/include/asm/cpufeature.h +LIB_H += util/include/asm/unistd_32.h +LIB_H += util/include/asm/unistd_64.h LIB_H += perf.h LIB_H += util/annotate.h LIB_H += util/cache.h diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 16e7d20..3afa39a 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -10,6 +10,9 @@ void get_term_dimensions(struct winsize *ws); #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define CPUINFO_PROC "model name" +#ifndef __NR_perf_event_open +# define __NR_perf_event_open 336 +#endif #endif #if defined(__x86_64__) @@ -17,6 +20,9 @@ void get_term_dimensions(struct winsize *ws); #define rmb() asm volatile("lfence" ::: "memory") #define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define CPUINFO_PROC "model name" +#ifndef __NR_perf_event_open +# define __NR_perf_event_open 298 +#endif #endif #ifdef __powerpc__ diff --git a/tools/perf/util/include/asm/unistd_32.h b/tools/perf/util/include/asm/unistd_32.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/perf/util/include/asm/unistd_32.h @@ -0,0 +1 @@ + diff --git a/tools/perf/util/include/asm/unistd_64.h b/tools/perf/util/include/asm/unistd_64.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/perf/util/include/asm/unistd_64.h @@ -0,0 +1 @@ + -- cgit v1.1