From eac9eacee1602710dda47c517ad0b61ac6f429bf Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 17:07:24 +0200 Subject: perf tools: Check we are able to read the event size on mmap Check we have enough mmaped space to read the current event size from its headers, otherwise we may dereference some hell there. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/util/session.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fff66741f18d..61746b5866d8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1007,6 +1007,13 @@ remap: file_pos = file_offset + head; more: + /* + * Ensure we have enough space remaining to read + * the size of the event in the headers. + */ + if (head + sizeof(event->header) > mmap_size) + goto remap; + event = (union perf_event *)(buf + head); if (session->header.needs_swap) -- cgit v1.2.2 From dd5f5fd1083601d9145168ce43a268a068add81a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 17:07:24 +0200 Subject: perf tools: Remove junk code in mmap size handling size is overriden later and used only then. Those lines are only junk, probably a leftover. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/util/session.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 61746b5866d8..db652068c396 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1018,9 +1018,6 @@ more: if (session->header.needs_swap) perf_event_header__bswap(&event->header); - size = event->header.size; - if (size == 0) - size = 8; if (head + event->header.size > mmap_size) { if (mmaps[map_idx]) { -- cgit v1.2.2 From 74429964d8e29c0107fa6e9cdf35b8f33f57405d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 17:49:00 +0200 Subject: perf tools: Move evlist sample helpers to evlist area These APIs should belong to evlist.c as they may not be exclusively tied to the headers. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian entries, node) { + if (!type) + type = pos->attr.sample_type; + else if (type != pos->attr.sample_type) + die("non matching sample_type"); + } + + return type; +} + +bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) +{ + bool value = false, first = true; + struct perf_evsel *pos; + + list_for_each_entry(pos, &evlist->entries, node) { + if (first) { + value = pos->attr.sample_id_all; + first = false; + } else if (value != pos->attr.sample_id_all) + die("non matching sample_id_all"); + } + + return value; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 8b1cb7a4c5f1..a8556b68c392 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -65,4 +65,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__set_filters(struct perf_evlist *evlist); +u64 perf_evlist__sample_type(struct perf_evlist *evlist); +bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); + #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 93862a8027ea..0717bebc7649 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -934,37 +934,6 @@ out_delete_evlist: return -ENOMEM; } -u64 perf_evlist__sample_type(struct perf_evlist *evlist) -{ - struct perf_evsel *pos; - u64 type = 0; - - list_for_each_entry(pos, &evlist->entries, node) { - if (!type) - type = pos->attr.sample_type; - else if (type != pos->attr.sample_type) - die("non matching sample_type"); - } - - return type; -} - -bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) -{ - bool value = false, first = true; - struct perf_evsel *pos; - - list_for_each_entry(pos, &evlist->entries, node) { - if (first) { - value = pos->attr.sample_id_all; - first = false; - } else if (value != pos->attr.sample_id_all) - die("non matching sample_id_all"); - } - - return value; -} - int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, perf_event__handler_t process, struct perf_session *session) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 456661d7f10e..1886256768a1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -64,8 +64,6 @@ int perf_header__write_pipe(int fd); int perf_header__push_event(u64 id, const char *name); char *perf_header__find_event(u64 id); -u64 perf_evlist__sample_type(struct perf_evlist *evlist); -bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); void perf_header__set_feat(struct perf_header *header, int feat); void perf_header__clear_feat(struct perf_header *header, int feat); bool perf_header__has_feat(const struct perf_header *header, int feat); -- cgit v1.2.2 From a285412479b6d5af3e48273a92ec2f1987df8cd1 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 19:33:04 +0200 Subject: perf tools: Pre-check sample size before parsing Check that the total size of the sample fields having a fixed size do not exceed the one of the whole event. This robustifies the sample parsing. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/builtin-test.c | 4 +++- tools/perf/util/event.c | 16 ++++++++++++++++ tools/perf/util/event.h | 12 +++++++++++- tools/perf/util/evsel.c | 6 +++++- tools/perf/util/python.c | 5 +++-- tools/perf/util/session.c | 1 + tools/perf/util/session.h | 2 ++ 7 files changed, 41 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 11e3c8458362..44d7df280430 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -474,6 +474,7 @@ static int test__basic_mmap(void) unsigned int nr_events[nsyscalls], expected_nr_events[nsyscalls], i, j; struct perf_evsel *evsels[nsyscalls], *evsel; + int sample_size = perf_sample_size(attr.sample_type); for (i = 0; i < nsyscalls; ++i) { char name[64]; @@ -558,7 +559,8 @@ static int test__basic_mmap(void) goto out_munmap; } - perf_event__parse_sample(event, attr.sample_type, false, &sample); + perf_event__parse_sample(event, attr.sample_type, sample_size, + false, &sample); evsel = perf_evlist__id2evsel(evlist, sample.id); if (evsel == NULL) { pr_debug("event with id %" PRIu64 diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1023f67633a4..17c1c3c875c3 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -35,6 +35,22 @@ const char *perf_event__name(unsigned int id) return perf_event__names[id]; } +int perf_sample_size(u64 sample_type) +{ + u64 mask = sample_type & PERF_SAMPLE_MASK; + int size = 0; + int i; + + for (i = 0; i < 64; i++) { + if ((mask << i) & 1) + size++; + } + + size *= sizeof(u64); + + return size; +} + static struct perf_sample synth_sample = { .pid = -1, .tid = -1, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9c35170fb379..c08332871408 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -56,6 +56,13 @@ struct read_event { u64 id; }; + +#define PERF_SAMPLE_MASK \ + (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ + PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ + PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ + PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) + struct sample_event { struct perf_event_header header; u64 array[]; @@ -75,6 +82,8 @@ struct perf_sample { struct ip_callchain *callchain; }; +int perf_sample_size(u64 sample_type); + #define BUILD_ID_SIZE 20 struct build_id_event { @@ -178,6 +187,7 @@ int perf_event__preprocess_sample(const union perf_event *self, const char *perf_event__name(unsigned int id); int perf_event__parse_sample(const union perf_event *event, u64 type, - bool sample_id_all, struct perf_sample *sample); + int sample_size, bool sample_id_all, + struct perf_sample *sample); #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d6fd59beb860..bfce8bf642fa 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -304,7 +304,8 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type, } int perf_event__parse_sample(const union perf_event *event, u64 type, - bool sample_id_all, struct perf_sample *data) + int sample_size, bool sample_id_all, + struct perf_sample *data) { const u64 *array; @@ -319,6 +320,9 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, array = event->sample.array; + if (sample_size + sizeof(event->header) > event->header.size) + return -EFAULT; + if (type & PERF_SAMPLE_IP) { data->ip = event->ip.ip; array++; diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 8b0eff8b8283..4174c0990320 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -690,8 +690,9 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, return PyErr_NoMemory(); first = list_entry(evlist->entries.next, struct perf_evsel, node); - perf_event__parse_sample(event, first->attr.sample_type, sample_id_all, - &pevent->sample); + perf_event__parse_sample(event, first->attr.sample_type, + perf_sample_size(first->attr.sample_type), + sample_id_all, &pevent->sample); return pyevent; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index db652068c396..8940fd871eae 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -97,6 +97,7 @@ out: void perf_session__update_sample_type(struct perf_session *self) { self->sample_type = perf_evlist__sample_type(self->evlist); + self->sample_size = perf_sample_size(self->sample_type); self->sample_id_all = perf_evlist__sample_id_all(self->evlist); perf_session__id_header_size(self); } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 8daaa2d15396..66d4e1490879 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -43,6 +43,7 @@ struct perf_session { */ struct hists hists; u64 sample_type; + int sample_size; int fd; bool fd_pipe; bool repipe; @@ -159,6 +160,7 @@ static inline int perf_session__parse_sample(struct perf_session *session, struct perf_sample *sample) { return perf_event__parse_sample(event, session->sample_type, + session->sample_size, session->sample_id_all, sample); } -- cgit v1.2.2 From 98e1da905cbe64bb023a165c7c01eef5e800609e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 20:08:15 +0200 Subject: perf tools: Robustify dynamic sample content fetch Ensure the size of the dynamic fields such as callchains or raw events don't overlap the whole event boundaries. This prevents from dereferencing junk if the given size of the callchain goes too eager. Reported-by: Linus Torvalds Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/util/evsel.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bfce8bf642fa..ee0fe0dffa71 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -303,6 +303,17 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type, return 0; } +static bool sample_overlap(const union perf_event *event, + const void *offset, u64 size) +{ + const void *base = event; + + if (offset + size > base + event->header.size) + return true; + + return false; +} + int perf_event__parse_sample(const union perf_event *event, u64 type, int sample_size, bool sample_id_all, struct perf_sample *data) @@ -373,14 +384,29 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_CALLCHAIN) { + if (sample_overlap(event, array, sizeof(data->callchain->nr))) + return -EFAULT; + data->callchain = (struct ip_callchain *)array; + + if (sample_overlap(event, array, data->callchain->nr)) + return -EFAULT; + array += 1 + data->callchain->nr; } if (type & PERF_SAMPLE_RAW) { u32 *p = (u32 *)array; + + if (sample_overlap(event, array, sizeof(u32))) + return -EFAULT; + data->raw_size = *p; p++; + + if (sample_overlap(event, p, data->raw_size)) + return -EFAULT; + data->raw_data = p; } -- cgit v1.2.2 From 5538becaec9ca2ff21e7826372941dc46f498487 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 22 May 2011 02:17:22 +0200 Subject: perf tools: Propagate event parse error handling Better handle event parsing error by propagating the details in upper layers or by dumping some failure message. So that the user knows he has some crazy events in the batch. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/builtin-test.c | 9 +++++++-- tools/perf/builtin-top.c | 7 ++++++- tools/perf/util/python.c | 14 ++++++++++---- tools/perf/util/session.c | 14 ++++++++++---- 4 files changed, 33 insertions(+), 11 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 44d7df280430..1fa9f58c2af2 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -559,8 +559,13 @@ static int test__basic_mmap(void) goto out_munmap; } - perf_event__parse_sample(event, attr.sample_type, sample_size, - false, &sample); + err = perf_event__parse_sample(event, attr.sample_type, sample_size, + false, &sample); + if (err) { + pr_err("Can't parse sample, err = %d\n", err); + goto out_munmap; + } + evsel = perf_evlist__id2evsel(evlist, sample.id); if (evsel == NULL) { pr_debug("event with id %" PRIu64 diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7e3d6e310bf8..74f533cbf6ca 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -805,9 +805,14 @@ static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) { struct perf_sample sample; union perf_event *event; + int ret; while ((event = perf_evlist__read_on_cpu(top.evlist, cpu)) != NULL) { - perf_session__parse_sample(self, event, &sample); + ret = perf_session__parse_sample(self, event, &sample); + if (ret) { + pr_err("Can't parse sample, err = %d\n", ret); + continue; + } if (event->header.type == PERF_RECORD_SAMPLE) perf_event__process_sample(event, &sample, self); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4174c0990320..3344d6e6f048 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -675,6 +675,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, union perf_event *event; int sample_id_all = 1, cpu; static char *kwlist[] = {"sample_id_all", NULL, NULL}; + int err; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &cpu, &sample_id_all)) @@ -690,12 +691,17 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, return PyErr_NoMemory(); first = list_entry(evlist->entries.next, struct perf_evsel, node); - perf_event__parse_sample(event, first->attr.sample_type, - perf_sample_size(first->attr.sample_type), - sample_id_all, &pevent->sample); + err = perf_event__parse_sample(event, first->attr.sample_type, + perf_sample_size(first->attr.sample_type), + sample_id_all, &pevent->sample); + if (err) { + pr_err("Can't parse sample, err = %d\n", err); + goto end; + } + return pyevent; } - +end: Py_INCREF(Py_None); return Py_None; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8940fd871eae..948327d9e92b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -480,6 +480,7 @@ static void flush_sample_queue(struct perf_session *s, struct perf_sample sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; + int ret; if (!ops->ordered_samples || !limit) return; @@ -488,9 +489,12 @@ static void flush_sample_queue(struct perf_session *s, if (iter->timestamp > limit) break; - perf_session__parse_sample(s, iter->event, &sample); - perf_session_deliver_event(s, iter->event, &sample, ops, - iter->file_offset); + ret = perf_session__parse_sample(s, iter->event, &sample); + if (ret) + pr_err("Can't parse sample, err = %d\n", ret); + else + perf_session_deliver_event(s, iter->event, &sample, ops, + iter->file_offset); os->last_flush = iter->timestamp; list_del(&iter->list); @@ -806,7 +810,9 @@ static int perf_session__process_event(struct perf_session *session, /* * For all kernel events we get the sample data */ - perf_session__parse_sample(session, event, &sample); + ret = perf_session__parse_sample(session, event, &sample); + if (ret) + return ret; /* Preprocess sample records - precheck callchains */ if (perf_session__preprocess_sample(session, event, &sample)) -- cgit v1.2.2